Paths

setwd("/Users/jonasamar/Stabl/Drug Study/Rscripts")
OOL_path <- "/Users/jonasamar/Stabl/Drug Study/Onset of Labor csv"
drug_assay_path <- "/Users/jonasamar/Stabl/Drug Study/Drug assay csv"

Libraries

library(tidyverse)
library(tidyselect)
library(reshape2)
library(dplyr)
library(pheatmap)
library(RColorBrewer)
library(viridis)
library(paletteer)
library(tidyr)
library(janitor)
library(lme4)
library(lmerTest)
library(utils)
library(ggplot2)
library(r2glmm)
library(sjstats)
library(gridExtra)

Loading Data and Models

OOL data and models

# df_model
load(paste0(OOL_path, "/pen_model_curves_cytof.rda"))
# curve.classification <- df_sum
curve.classification <- read.csv(paste0(OOL_path, "/pen_classification_curves_cytof.csv"))
# penalized dataset with outcomes (DOS) ante partum
OOL_data <- read.csv(paste0(OOL_path, "/immunome_noEGA_DOS_pen_OOL.csv")) %>%   
            filter(DOS <= 0) # we only are interested in DOS <=0
# outcomes
DOS <- OOL_data$DOS
# results from univariate analysis on OOL data
spearman_cor <- read_csv(paste0(OOL_path, "/Univariate regated OOL/SpearmanCorrelationsPval.csv"))
New names:Rows: 768 Columns: 3── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): ...1
dbl (2): Spearman corr, pvalue
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# feature.index
load(paste0(OOL_path, "/feature_index.rda"))

Drug Assay data and models

# model.scores
load(paste0(drug_assay_path, "/model scores.rda"))
# pseudo_log10.individual.linear.model
load(paste0(drug_assay_path, "/pseudo_log10 median linear models.rda"))
# pseudo_log10.median.linear.model
load(paste0(drug_assay_path, "/pseudo_log10 individual linear models.rda"))
# MedianDosereponse
load(paste0(drug_assay_path, "/median pen dose response with scales.rda"))
# AllPenDosereponse
load(paste0(drug_assay_path, "/all pen dose response with scales.rda"))

Scores

Here we want to compare the drug effect on each feature to the “normal” behavior of the feature modeled in OOL study.

Auxiliaries score functions

drug.effect

# Function which return:
# +1 for activating drugs
# -1 for inhibiting drugs
# 0 for non active (or uncertain) drugs
# based on the slope of the model and its p-value

drug.effect <- function(slope, pval, slope.threshold, pval.threshold){
  if ((abs(slope) > slope.threshold) & (pval < pval.threshold) & (!is.na(slope)) & (!is.na(pval))){
    return(slope/abs(slope))
  }
  else{
    return(0)
  }
}

feature.normal.behavior.at.TTL

# Function which returns
# 1 if the model (of the OOL feature) is increasing at TTL
# -1 if the model (of the OOL feature) is decreasing at TTL
# NA if the behavior of the feature is inconclusive (missing model or too weak slope)
# based on the model (contained in a list) itself and its p-value

feature.normal.behavior.at.TTL <- function(model, pval, slope.threshold, pval.threshold, TTL){
  if (length(pval) == 0 || is.na(pval) ||pval > pval.threshold || length(model) == 0){
    return(NA)
  }
  else{
    # Here we focus un the derivative around 0 to estimate the behavior of the feature
    model <- model[[1]]
    quadratic <- (length(model) == 3)

    # Derivative of the model at TTL
    # for quadratic model
    if (quadratic){
      deriv <- 2*model[['DOS2']]*TTL + model[['DOS']]
    } 
    # for linear model
    else{
      deriv <- model[['DOS']]
    }
    
    # Returning effect coefficient
    if (abs(deriv) > slope.threshold){
      return(deriv/abs(deriv))
    }
    else{
      return(NA)
    }
  }
}

create.drug.effect.data.frame

# Function which creates a dataframe with 
# 1 when the drug is affecting the feature the opposite direction of its normal behavior at TTL
# -1 when the drug is affecting the feature the same direction of its normal behavior at TTL
# 0 when the effect of the drug is null
# NA when behavior of the feature is null or hard to estimate (high p-value) or if we miss data in the drug assay study

create.drug.effect.data.frame <- function(drug.name,
                                          normal.behavior.pval.threshold, 
                                          normal.behavior.slope.threshold, 
                                          drug.pval.threshold, 
                                          drug.slope.threshold,
                                          TTL,
                                          features.of.interest){
  
  # Dataframe feature ~ stimulation containing the final effect on a drug on each feature under each stimulation
  drug.effect.dataframe <- model.scores %>%
                        filter(drug == drug.name) %>%
                        rowwise() %>%
                        mutate(drug.behavior = drug.effect(slope, 
                                                            pval, 
                                                            slope.threshold = drug.slope.threshold, 
                                                            pval.threshold = drug.pval.threshold),
                               population = str_extract(feature, "^[^_]+"),
                               reagent_stim = str_extract(feature, "(?<=_).*$")) %>%
                        select(population, drug.behavior, reagent_stim) %>%
                        pivot_wider(names_from = reagent_stim, values_from = drug.behavior)
  
  # Concatenation of all information on the feature models + normal.behavior = +1, -1 or NA
  model.info <- merge(df_models, curve.classification, by.x = "feature", by.y = "cytof") %>%
                rowwise() %>%
                mutate(normal.behavior = feature.normal.behavior.at.TTL(model,
                                                                        pval, 
                                                                        normal.behavior.slope.threshold,
                                                                        normal.behavior.pval.threshold,
                                                                        TTL),
                       # Adding some nuance to the importance of the behavior
                       normal.behavior = ifelse(feature %in% features.of.interest,
                                                normal.behavior,
                                                normal.behavior/2))

  # Dataframe feature ~ stimulation containing the values of normal.behavior
  behavior.dataframe <- model.info %>%
                          mutate(population = str_extract(feature, "^[^_]+"),
                                 reagent_stim = str_extract(feature, "(?<=_).*$")) %>%
                          select(population, normal.behavior, reagent_stim) %>%
                          pivot_wider(names_from = reagent_stim, values_from = normal.behavior) %>%
                          # removing features not measured in both studies (drugs and OOL)
                          # `STAT3_LPS`, `STAT5_LPS`, `STAT1_LPS`, `STAT6_LPS`, `STAT1_GMCSF`
                          # "STAT1_unstim"
                          filter(population %in% drug.effect.dataframe$population) %>%
                          select(one_of(colnames(drug.effect.dataframe)))
                        
  # Dataframe feature ~ stimulation containing the product of behavior.matrix and drug.effect.matrix
  drug.final.effect.dataframe<- merge(pivot_longer(select(behavior.dataframe, 
                                                           one_of(colnames(drug.effect.dataframe))), 
                                                   cols = -population, 
                                                   names_to = "reagent_stim", 
                                                   values_to = "normal.behavior"),
                                      pivot_longer(select(drug.effect.dataframe,
                                                          one_of(colnames(behavior.dataframe))), 
                                                   cols = -population, 
                                                   names_to = "reagent_stim", 
                                                   values_to = "drug.behavior"),
                                      by = c("population", "reagent_stim")) %>%
                                rowwise() %>%
                                mutate(drug.final.effect = ifelse(is.na(normal.behavior) ||
                                                                    is.na(drug.behavior),
                                                                  NA,
                                                                  -normal.behavior*drug.behavior)) %>%
                                select(population, reagent_stim, drug.final.effect) %>%
                                pivot_wider(names_from = reagent_stim, values_from = drug.final.effect)

  return(drug.final.effect.dataframe)
}

Features of interest

For now, we don’t have a ML model with selected features helping us discriminate which features are more important to look at so we are looking at two sets of features : - the top 15 Cytof features from the OOL paper - the features from the univariate analysis with an absolute Spearman score > 0.3

Top 15 features from OOL paper

# Top immunome features from the OOL study
# we associate a coeff +1 (increasing) or -1 (decreasing) to the features
OOL.top.immunome.features <- list("CD56loCD16posNK_STAT1_IFNa"=1,
                                  # Granulocytes missing in the drug assay study
                                  "CD56hiCD16negNK_STAT1_IFNa"=1,
                                  "CD4Tnaive_MAPKAPK2_IFNa"=1,
                                  "ncMCs_CREB_GMCSF"=-1,
                                  "CD8Tcm_MAPKAPK2_unstim"=1, #-1 at TTL = 0
                                  "CD8Tem_MAPKAPK2_unstim"=1, #-1 at TTL = 0
                                  "pDCs_STAT1_IFNa"=1,
                                  "Bcells_MAPKAPK2_LPS"=1,
                                  "CD4Tem_MAPKAPK2_unstim"=1, #-1 at TTL = 0
                                  "CD8Tcm_MAPKAPK2_IFNa"=1, 
                                  "CD8Tem_MAPKAPK2_IFNa"=1,
                                  # Bcells missing in the drug assay study
                                  "CD4Tem_NFkB_IL246"=-1,
                                  "CD4Tcm_IkB_unstim"=1, #-1 at TTL = 0
                                  "mDCs_STAT6_IFNa"=1, #-1 at TTL = 0
                                  "pDCs_STAT6_IFNa"=1, #-1 at TTL = 0
                                  "mDCs_MAPKAPK2_unstim"=-1, 
                                  "pDCs_MAPKAPK2_unstim"=-1
                                  )

Top features from the univariate analysis

# Top immunome features from the univariate analysis on OOL data
# we associate a coeff +1 (increasing) or -1 (decreasing) to the features

univariate.OOL.features <- apply(spearman_cor[abs(spearman_cor$`Spearman corr`) > 0.3,], # taking features with |Spearmanr| > 0.3
                                 1,
                                 function(row){ # associating the feature name to +1 (increasing) or -1 (decreasing)
                                   key <- row[[1]]
                                   if (!is.na(key)) {
                                     value <- as.numeric(row[[2]])/abs(as.numeric(row[[2]]))
                                     setNames(list(value), key)
                                   } 
                                 }) %>%
                                Filter(function(x) !is.null(x), .) %>%
                                unlist(recursive=FALSE)

Top features from feature.index

# Top immunome features from the feature.index
# we associate a coeff +1 (increasing) or -1 (decreasing) to the features

# Timepoint of interest
TTL <- -84

# Thresholds to calculate the normal behavior of a given feature
normal.behavior.slope.threshold <- 0.
normal.behavior.pval.threshold <- 10

# Info (model, pval, rmse, ...) of the models at the timepoint TTL
model.info <- merge(df_models, curve.classification, by.x = "feature", by.y = "cytof") %>%
              rowwise() %>%
              mutate(normal.behavior = feature.normal.behavior.at.TTL(model,
                                                                      pval, 
                                                                      normal.behavior.slope.threshold,
                                                                      normal.behavior.pval.threshold,
                                                                     TTL))
# List of the features and their behavior
top.feature.index <- feature.index %>%
                        filter(model_index > 0) %>%
                        left_join(model.info, by="feature") %>%
                        select(feature, normal.behavior) %>%
                        pull(normal.behavior) %>% 
                        setNames(pull(feature.index[(feature.index$model_index > 0) & !(is.na(feature.index$model_index)),], 
                                      feature)) %>%
                        as.list()

Drugs ranking

Here we rank the drugs based on their effect on the chosen features of interest. We associate a +1 score when the drug has an effect which is opposite to the “normal” behavior/ trend of the feature and -1 when the effect of the drug is colinear to the “normal” feature behavior.

# Models of interest
final.model <- pseudo_log10.median.linear.model
final.model.type <- "median" #"individual" or "mixed" or "median"

# Chosing features of interest
feature_choice <- "top_feature_index_features"
OOL_features <- top.feature.index

# Chosing thresholds over or under which we consider the effect of the drug as relevant
slope.threshold <- 0
pval.threshold <- 1

Calculation of the scores and ranking of the features.

drug.ranking : dataframe containing the name of the drugs and their score.

drug.ranking <- data.frame(drug = character(),
                           score = numeric(),
                           stringsAsFactors = FALSE)

# Getting a normized log transform of the feature index to be used as coefficient in the score
norm.log.feature.index <- feature.index %>%
                            mutate(log.model_index = log10(1 + model_index),
                                   norm.log.model_index = log.model_index/sum(log.model_index, na.rm=TRUE)) %>%
                            select(feature, norm.log.model_index) %>%
                        pull(norm.log.model_index) %>% 
                        setNames(pull(feature.index, feature)) %>%
                        as.list()

# Calculating the score
for (drug in unique(final.model$drug)){
  score <- 0
  # We sum the scores over all the features for each drug
  for (feature in names(OOL_features)){
    # Getting the drug effect on the feature
    slope <- model.scores[(model.scores$feature == feature) & (model.scores$drug == drug),]$slope
    pval <- model.scores[(model.scores$feature == feature) & (model.scores$drug == drug),]$pval
    # Sometimes the values of slope and/or pval is empty so we fill with 0 these values
    drug_effect <- ifelse((length(slope)*length(pval) == 0) || (is.na(slope) | is.na(pval)),
                          0,
                          drug.effect(slope, pval, slope.threshold, pval.threshold))
    # Adding the individual scores of the drug each feature 
    score <- score - drug_effect*OOL_features[[feature]]*norm.log.feature.index[[feature]]
  }
  # Adding the drug and its score to drug.ranking
  drug.ranking <- rbind(drug.ranking,
                        data.frame(drug=drug,
                                   score=score,
                                   stringsAsFactors = FALSE))
}
# Arranging the drugs to see the best drugs on top
drug.ranking <- drug.ranking %>% arrange(desc(score))
drug.ranking

Visualization

Models versus “normal” behavior

Auxiliary function to plot the mixed model.

# Function taking the name of a feature, name of a drug and a pvalue
# Returning a plot of the model corresponding to the feature and drug

plot_drug_feature_model <- function(target_drug, target_feature, p_value){
  # Getting the model associated to the target_drug and the target_feature
  target_model <- final.model$model[final.model$feature == target_feature & final.model$drug == target_drug][[1]]
  # Getting the pval associated with the target_model
  pval <- final.model$pval[final.model$feature == target_feature & final.model$drug == target_drug][[1]]
  # Setting the dose values to their pseudo log transform
  Doseresponse <- AllPenDoseresponse %>%
                    mutate(dose = pseudo_log_dose,
                           ID = as.factor(ID)) %>%
                    filter(is.finite(dose))
  # Subset of the data for the chosen feature and drug
  data <- Doseresponse %>% 
              filter(feature == !!target_feature,
                     drug == !!target_drug) %>%
              select(ID, dose, value)
    
  ### PLOT THE MEDIAN MODEL
  if (final.model.type == "median"){
    # Coefficients of the selected model
    model_coefs <- coef(target_model) %>%
                      as.data.frame() %>%
                      t() %>%
                      `colnames<-`(c("Intercept", "Slope"))
    
    data_rani <- merge(data, model_coefs)
    
    # Plot
    plot <- ggplot(data_rani, aes(x=dose, y=value, group=dose)) +
              geom_boxplot() +
              geom_abline(aes(intercept = Intercept, 
                              slope = Slope),
                          size = 1.) +
              theme(legend.position = "top")
  }
  
  ### PLOT THE INDIVIDUAL MODEL
  if (final.model.type == "individual"){
    # Coefficients of the selected model
    model_coefs <- target_models %>%
                      mutate(Slope = sapply(model, function(x) coef(x)["dose"]),
                      Intercept = sapply(model, function(x) coef(x)["(Intercept)"]),
                      ID = as.factor(ID))
    
    data_rani <- left_join(data, model_coefs, by = "ID")
    
    # Plot
    plot <- ggplot(data = data_rani, mapping = aes(x = dose, y = value, colour = ID)) +
              geom_point(na.rm = T, alpha = 0.5) +
              geom_abline(aes(intercept = Intercept, slope = Slope, colour = ID), 
                          size = 1.) +
              theme(legend.position = "top")
  }
  
  ### PLOT THE MIXED LINEAR MODEL
  if (final.model.type == "mixed"){
    # Coefficients of the selected model
    model_coefs <- coef(target_model)$ID %>% 
                      rename(Intercept = `(Intercept)`, Slope = dose) %>% 
                      rownames_to_column("ID")
    
    data_rani <- left_join(data, model_coefs, by = "ID")
    
    # Plot
    plot <- suppressWarnings(ggplot(data = data_rani, 
                                    mapping = aes(x = dose, 
                                                  y = value, 
                                                  colour = ID)) +
                               geom_point(na.rm = T, alpha = 0.5) +
                               geom_abline(aes(intercept = Intercept, 
                                               slope = Slope,
                                               colour = ID),
                                           size = 1.))
  }
  
  # Removing unecessary labels and adding title
  plot <- plot + 
      ggtitle(paste(target_feature, pval, sep=",\np = ")) +
      xlab(NULL) +  # Remove x-axis label
      ylab(NULL) +  # Remove y-axis label
      theme(legend.position = "none") +
      theme(plot.title = element_text(size = 6))
  
  return(plot)
}

Plotting the individual models of the drug on the chosen features versus the normal behavior of the OOL feature in order to see if the trends are actually opposites (or colinear) and that the models fit the data.

# Selecting a specific drug and plotting the comparison of the model of the features with the mixed model of the drug
target_drug <- "Metformin"
# Time to labor of interest (will plot the derivative at this point)
TTL <- -84

plots <- list()
i <- 0
for (target_feature in names(OOL_features)) {
  target_model <- final.model$model[final.model$feature == target_feature & final.model$drug == target_drug]
  pval <- model.scores[(model.scores$feature == target_feature) & (model.scores$drug == target_drug),]$pval
  
  # We only plot features on which the score has been calculated (with mixed model having a pvalue < pval < pval.threshold)
  if ((length(pval) > 0) &&
      (!is.na(pval)) &&
      (pval < pval.threshold) && 
      (length(target_model)>0)){
    # Counting the number of plots
    i <- i + 2
    
    ### PLOT THE INDIVIDUAL LINEAR MODEL
    model_coef_plot <- plot_drug_feature_model(target_drug, target_feature, pval)
    # Adding plot to the list of plots
    plots[[target_feature]] <- model_coef_plot

    
    ### PLOT THE ASSOCIATED OOL FEATURE
    # Getting the coefficients and class of the model
    coefs <- df_models[df_models$feature == target_feature,]$model[[1]][[1]]
    
    # Building the predictions
    y <- OOL_data[, target_feature]
    intercept <- coefs[['(Intercept)']]
    if (length(coefs) == 3){ # quadratic model
  
      a <- coefs[['DOS2']]
      b <- coefs[['DOS']]
      y_pred <- intercept + b * DOS + a * DOS^2
      y_deriv <- (2*a*TTL + b)*(DOS - TTL) + (intercept + b * TTL + a * TTL^2)
    }
    else{ # length(coefs) == 2, linear model
      slope <- coefs[['DOS']]
      y_pred <- intercept + slope * DOS
      y_deriv <- slope*(DOS - TTL) + (intercept + slope * TTL)
    }
    df <- data.frame(x = DOS, y = y, y_pred = y_pred, y_deriv = y_deriv)
    
    # Getting pvalue of the model
    pval <- curve.classification[curve.classification$cytof == target_feature,]$pval
    # Plot
    plot <- ggplot(df, aes(x = x, y = y)) +
      geom_point(color = "blue", alpha = 0.5, size = 1) +
      geom_line(aes(y = y_pred), color = "red", size = 0.7) +
      geom_line(aes(y = y_deriv), linetype = "dashed", color = "red", size = 0.7) +
      geom_vline(xintercept = TTL, linetype = "dashed", color = "red", size = 0.7) +
      ggtitle(paste(target_feature, pval, sep=",\np = ")) +
      xlab(NULL) +  # Remove x-axis label
      ylab(NULL) +  # Remove y-axis label
      theme(plot.title = element_text(size = 6))
    # Adding plot to the list of plots
    plots[[paste0(target_feature,".OOL")]] <- plot
  }
}

# Saving the plots
pdf(paste(drug_assay_path, "/plots/", target_drug, "_on_", feature_choice,".pdf", sep = ""))
for (j in 1:ceiling(i/16)){
  grid.arrange(grobs = plots[(16*(j-1)+1):min(16*j,i)], nrow = 4, ncol = 4)
}
dev.off()
null device 
          1 

Global visualization : heatmap

# Function to plot the pheatmap of the final drug effect
plot_pheatmap <- function(drug.name, drug.final.effect.dataframe, clustering_rows, clustering_cols, dendro=FALSE){
  my_colors <- c("blue", rgb(0, 0, 1, alpha = 0.5), "white", rgb(1, 0, 0, alpha = 0.5), "red")
  
  # Preparing the data
  data <- as.matrix(drug.final.effect.dataframe[, -1])
  rownames(data) <- drug.final.effect.dataframe$population
  data[is.na(data)] <- 0
  
  # Reorganizing rows and columns
  row_order <- order.dendrogram(as.dendrogram(clustering_rows))
  col_order <- order.dendrogram(as.dendrogram(clustering_cols))
  data <- data[row_order, col_order]
  
  # Plot
  pheatmap(data, 
           color = my_colors,
           na_color = "grey",
           cluster_rows = dendro, 
           cluster_cols = dendro,
           main = drug.name,
           cellwidth = 10, 
           cellheight = 10,
           legend_breaks = c(-1, -0.5, 0, 0.5, 1),
           legend_labels = c("Drug effect colinear\nto normal pregnancy",
                             "",
                             "Missing Value\nor\nNot significant",
                             "",
                             "Drug effect opposite\nto normal pregnancy")
           )
}

Saving the heat maps for a chosen set of thresholds.

# Parameters for the heat map scores
TTL <- -84
normal.behavior.pval.threshold <- 1.
normal.behavior.slope.threshold <- 0.
drug.pval.threshold <- 1.
drug.slope.threshold <- 0.
features.of.interest <- names(top.feature.index)
ref.clustering.feature <- "Metformin"

# Building the clustering of reference for all the heatmaps
ref_data <- create.drug.effect.data.frame(
                  ref.clustering.feature,
                  normal.behavior.pval.threshold, 
                  normal.behavior.slope.threshold, 
                  drug.pval.threshold, 
                  drug.slope.threshold,
                  TTL,
                  features.of.interest)
data <- as.matrix(ref_data[, -1])
rownames(data) <- ref_data$population
data[is.na(data)] <- 0
clustering_rows <- hclust(dist(data))
clustering_cols <- hclust(dist(t(data)))


# Plotting all the heat maps
pdf(paste(drug_assay_path, "/plots/Drug effect heatmaps TTL=",TTL, ".pdf", sep=""), width = 12, height = 6)
for (target_drug in unique(final.model$drug)){
  plot_pheatmap(target_drug, 
                create.drug.effect.data.frame(
                  target_drug,
                  normal.behavior.pval.threshold, 
                  normal.behavior.slope.threshold, 
                  drug.pval.threshold, 
                  drug.slope.threshold,
                  TTL,
                  features.of.interest),
                clustering_rows,
                clustering_cols,
                dendro=FALSE)
}
dev.off()
null device 
          1 

Global visualization : drug effects

Here we plot a figure helping us visualize the effect of one drug based on the slope of its linear model across all features.

slope_threshold <- 2e-1 # Above this threshold the color of the plot will be the most intense one
pval_threshold <- 5e-2 # Under this threshold the size of the node will be the biggest one (if taking pvalue for size)
rmse_threshold <- 1e-2 # Under this threshold the size of the node will be the biggest one (if taking rmse for size)
precision <- 0. # Under this threshold the values in the dataset on which the model is built is not relevant (Slope_intensity <- 0)

final.model.info.decomp <- final.model %>%
                        group_by(feature, drug) %>%
                        slice(1) %>%
                        mutate(# Retrieving the separation : population, reagent, stimulation
                               population = sub("^(.*?)_.*", "\\1", as.character(feature)),
                               reagent = sub(".*?_(.*?)_.*", "\\1", as.character(feature)),
                               stimulation = sub(".*_.*_(.*)", "\\1", as.character(feature)),

                               # If the absolute value of the slope is greater than the threshold, 
                               # the slope is replaced by the threshold (with the adequate sign)
                               Slope = ifelse(slope > slope_threshold, slope_threshold, slope),
                               Slope = ifelse(slope < -slope_threshold, -slope_threshold, slope),
                               # If the pvalue is smaller than the threshold, 
                               # the pvalue is replaced by the threshold
                               pval = ifelse(pval < pval_threshold, pval_threshold, pval),
                               # log transform normalized by the threshold
                               log_pval = log10(pval)/log10(pval_threshold),
                               # If the rmse is smaller than the threshold, 
                               # the rmse is replaced by the threshold
                               RMSE = ifelse(rmse < rmse_threshold, rmse_threshold, rmse),
                               # log transform normalized by the threshold
                               log_RMSE = log10(RMSE)/log10(rmse_threshold),
                               # Slope_intensity = Slope/(max(subset data) - min(subset data)) only when the measures are significant
                               max_val = max(AllPenDoseresponse[AllPenDoseresponse$drug == drug & 
                                                                  AllPenDoseresponse$feature == feature,]$value),
                               min_val = min(AllPenDoseresponse[AllPenDoseresponse$drug == drug & 
                                                                  AllPenDoseresponse$feature == feature,]$value),
                               Slope_intensity = ifelse(max_val - min_val < precision,
                                                        0,
                                                        Slope/(max_val - min_val)),
                               
                               # Coordinates for the plot of the reagents
                               ## STAT first column
                               x = ifelse(grepl("STAT", reagent), 1.5, 0),
                               ## STAT1 (1,4)
                               y = ifelse(reagent == "STAT1", 4, 0),
                               ## STAT3 (1,3)
                               y = ifelse(reagent == "STAT3", 3, y),
                               ## STAT5 (1,2)
                               y = ifelse(reagent == "STAT5", 2, y),
                               ## STAT6 (1,1)
                               y = ifelse(reagent == "STAT6", 1, y),
                               ## NFkB (3,4)
                               x = ifelse(reagent == "NFkB", 3, x),
                               y = ifelse(reagent == "NFkB", 4, y),
                               ## ERK (3,3)
                               x = ifelse(reagent == "ERK", 3, x),
                               y = ifelse(reagent == "ERK", 3, y),
                               ## S6 (3,2)
                               x = ifelse(reagent == "S6", 3, x),
                               y = ifelse(reagent == "S6", 2, y),
                               ## MAPKAPK2 (3,1)
                               x = ifelse(reagent == "MAPKAPK2", 3, x),
                               y = ifelse(reagent == "MAPKAPK2", 1, y),
                               ## IkB (4,4)
                               x = ifelse(reagent == "IkB", 4, x),
                               y = ifelse(reagent == "IkB", 4, y),
                               ## CREB (4,3)
                               x = ifelse(reagent == "CREB", 4, x),
                               y = ifelse(reagent == "CREB", 3, y),
                               ## p38 (4,2)
                               x = ifelse(reagent == "p38", 4, x),
                               y = ifelse(reagent == "p38", 2, y))

Here we are doing one of the 100 plots that are going to be built for each drug so that we can plot the legend as well. Each node represent a reagent, its size is proportional to \(-log_{10}(p_{val})\), its color is

# Example of settings for the plot
example_drug <- "Folic acid"
example_population <- "CD4Tem"
example_stimulation <- "IFNa"

# Subset
df <- final.model.info.decomp[final.model.info.decomp$drug == example_drug & 
                                   final.model.info.decomp$population == example_population &
                                   final.model.info.decomp$stimulation == example_stimulation,]

# Plot
p <- ggplot(df, aes(x = x, y = y)) +
  # Nodes size, border and color
  geom_point(aes(size = log_pval, fill = Slope_intensity), shape = 21) +
  # Name of the nodes
  geom_text(aes(label = reagent), vjust = -1.5, hjust = 0.5, size = 5, color = "black") +
  # Scale of the size of the nodes
  scale_size_continuous(range = c(2, 10), limits = c(0, 1)) +
  # Scale of the color of the nodes
  scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 0, limits = c(-0.3, 0.3)) +
  # Legend settings (position and background)
  theme(legend.position = "bottom", legend.box = "horizontal", legend.key = element_blank()) +
  # Axis
  scale_x_continuous(limits = c(0.5, 5), expand = c(0, 0)) +  # Set x-axis limits and remove expansion
  scale_y_continuous(limits = c(0.5, 4.5), expand = c(0., 0.)) +
  # Lables
  labs(x = NULL, y = NULL, size = "log_pval", fill = "Slope_intensity", color = "Class")

# Saving the plot
ggsave(paste0(drug_assay_path, "/plots/legend_grid_effect_plot.png"), plot = p, width = 10, height = 8)

Plotting a grid composed of subplots like the one above for every population and avery stimulation and each drug

# Useful variables (we rewrite them because the order is important for the figures)
stimulations <- c("GMCSF", "IFNa", "IL246", "LPS", "unstim")
populations <- c("Bcells", "CD4Tcm", "CD4Teff", "CD4Tem", "CD4Tnaive", "CD4negCD8negTcells", "CD56hiCD16negNK", "CD56loCD16posNK", "CD8Tcm", "CD8Teff", "CD8Tem", "CD8Tnaive", "Granulocytes", "MDSCs", "NKT", "Tregs", "intMCs", "mDCs", "ncMCs", "pDCs")
Drugs <- c("Cefotaxime", "Lansoprazole", "Iopamidol", "Iohexol", "Benzylpenicillin", "Chlorthalidone", "Rifabutin", "Iodixanol", "Metformin", "Folic acid", "Clotrimazole", "Maprotiline", "Progesterone", "Pravastatin", "Methylpredonisolone")
features.of.interest <- names(top.feature.index)
  
plot_drug_grid_effect <- function(target_drug, features.of.interest){
  plot_list <- list()
  # Iterate over the rows and columns to create the plots
  pop_title <- TRUE
  for (stim in stimulations) {
    stim_title <- TRUE
    for (population in populations) {
      # Filter the data for the current population and stimulation
      subset_df <- final.model.info.decomp[final.model.info.decomp$drug == target_drug &
                                             final.model.info.decomp$population == population &
                                             final.model.info.decomp$stimulation==stim,] %>%
                    mutate(of.interest = ifelse(feature %in% features.of.interest,
                                                "Yes",
                                                "No"),
                           border.thickness = ifelse(feature %in% features.of.interest,
                                                     2.,
                                                     1.))
      
      # Subplot for the current population and stimulation
      p <- ggplot(subset_df, aes(x = x/25, y = y/25, color=of.interest, stroke=border.thickness)) +
        # Nodes size, border and color
        geom_point(aes(size = log_pval, fill = Slope_intensity, color=of.interest, stroke=border.thickness), shape = 21) +
        # Scale of the size of the nodes
        scale_size_continuous(range = c(2, 10), limits = c(0, 1)) +
        # Scale of the color of the nodes
        scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 0, limits = c(-0.3, 0.3)) +
        # Border of the nodes
        scale_color_manual(values = c("Yes" = "red")) +
        # Adding a border to the figure if there are features of interest inside
        geom_rect(aes(xmin = 0.02, xmax = 0.2, ymin = 0., ymax = 0.2),
                  fill = NA,
                  color = "red",
                  linetype = "solid",
                  size = ifelse(any(subset_df$of.interest == "Yes"), 1.5, NA)) +
        # Removing legends and adjusting size and margins
        theme(legend.position = "none",
          plot.title = element_text(size = 6, margin = margin(r = 0)),
          plot.margin = margin(0.5, 0., 0., 0.5),
          axis.text.x = element_blank(),
          axis.text.y = element_blank(),
          axis.ticks.x = element_blank(),
          axis.ticks.y = element_blank(),
          axis.title.y = element_text(size = 6, margin = margin(r = -2)),
          axis.text = element_text(size = 6)) +  
        # Labels (y label only appear for the first column of subplot with the name of the stim)
        labs(x = NULL, y = ifelse(stim_title, stim, ""), size = "log_pval", fill = "Slope_intensity", color = "Class") +
        # Axis
        scale_x_continuous(limits = c(0.02, 0.2), expand = c(0, 0)) +
        scale_y_continuous(limits = c(0., 0.2), expand = c(0., 0.)) +
        # Title (only appear for the first row of subplots with the name of the population)
        ggtitle(ifelse(pop_title, population, "")) +
        # Removing
        guides(fill = "none", color = "none", size = "none")
      
      # Store the plot in the plot list
      plot_list[[length(plot_list) + 1]] <- p
      stim_title <- FALSE
    }
    pop_title <- FALSE
  }
  
  # Creating the grid plot
  grid_arrange <- grid.arrange(grobs = plot_list,
                               ncol = length(populations),
                               nrow = length(stimulations))
  # Adding a title to the grid plot
  title <- textGrob(target_drug, gp = gpar(fontsize = 16, fontface = "bold"))
  layout <- rbind(c(1), c(2))
  arranged_plots <- arrangeGrob(title, grid_arrange, layout_matrix = layout, heights = c(1, 9))
  
  return(arranged_plots)
}

# Creating and saving the figures in pdf files
for (target_drug in Drugs) {
  grid_plot <- plot_drug_grid_effect(target_drug, features.of.interest)
  pdf(paste(drug_assay_path, "/plots/", target_drug, "_grid_effect_plots.pdf", sep=""), width=20, height=8)
  grid.draw(grid_plot)
  dev.off()
}

LS0tCnRpdGxlOiAiRHJ1ZyBhc3NheSBzY29yZXMgYW5kIHZpc3VhbGl6YXRpb24iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIFBhdGhzCgpgYGB7cn0Kc2V0d2QoIi9Vc2Vycy9qb25hc2FtYXIvU3RhYmwvRHJ1ZyBTdHVkeS9Sc2NyaXB0cyIpCk9PTF9wYXRoIDwtICIvVXNlcnMvam9uYXNhbWFyL1N0YWJsL0RydWcgU3R1ZHkvT25zZXQgb2YgTGFib3IgY3N2IgpkcnVnX2Fzc2F5X3BhdGggPC0gIi9Vc2Vycy9qb25hc2FtYXIvU3RhYmwvRHJ1ZyBTdHVkeS9EcnVnIGFzc2F5IGNzdiIKYGBgCgojIyBMaWJyYXJpZXMKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWR5c2VsZWN0KQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeSh2aXJpZGlzKQpsaWJyYXJ5KHBhbGV0dGVlcikKbGlicmFyeSh0aWR5cikKbGlicmFyeShqYW5pdG9yKQpsaWJyYXJ5KGxtZTQpCmxpYnJhcnkobG1lclRlc3QpCmxpYnJhcnkodXRpbHMpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShyMmdsbW0pCmxpYnJhcnkoc2pzdGF0cykKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoZ3JpZCkKYGBgCgojIyBMb2FkaW5nIERhdGEgYW5kIE1vZGVscwoKKipPT0wgZGF0YSBhbmQgbW9kZWxzKioKCmBgYHtyfQojIGRmX21vZGVsCmxvYWQocGFzdGUwKE9PTF9wYXRoLCAiL3Blbl9tb2RlbF9jdXJ2ZXNfY3l0b2YucmRhIikpCiMgY3VydmUuY2xhc3NpZmljYXRpb24gPC0gZGZfc3VtCmN1cnZlLmNsYXNzaWZpY2F0aW9uIDwtIHJlYWQuY3N2KHBhc3RlMChPT0xfcGF0aCwgIi9wZW5fY2xhc3NpZmljYXRpb25fY3VydmVzX2N5dG9mLmNzdiIpKQojIHBlbmFsaXplZCBkYXRhc2V0IHdpdGggb3V0Y29tZXMgKERPUykgYW50ZSBwYXJ0dW0KT09MX2RhdGEgPC0gcmVhZC5jc3YocGFzdGUwKE9PTF9wYXRoLCAiL2ltbXVub21lX25vRUdBX0RPU19wZW5fT09MLmNzdiIpKSAlPiUgICAKICAgICAgICAgICAgZmlsdGVyKERPUyA8PSAwKSAjIHdlIG9ubHkgYXJlIGludGVyZXN0ZWQgaW4gRE9TIDw9MAojIG91dGNvbWVzCkRPUyA8LSBPT0xfZGF0YSRET1MKIyByZXN1bHRzIGZyb20gdW5pdmFyaWF0ZSBhbmFseXNpcyBvbiBPT0wgZGF0YQpzcGVhcm1hbl9jb3IgPC0gcmVhZF9jc3YocGFzdGUwKE9PTF9wYXRoLCAiL1VuaXZhcmlhdGUgcmVnYXRlZCBPT0wvU3BlYXJtYW5Db3JyZWxhdGlvbnNQdmFsLmNzdiIpKQojIGZlYXR1cmUuaW5kZXgKbG9hZChwYXN0ZTAoT09MX3BhdGgsICIvZmVhdHVyZV9pbmRleC5yZGEiKSkKYGBgCgoqKkRydWcgQXNzYXkgZGF0YSBhbmQgbW9kZWxzKioKCmBgYHtyfQojIG1vZGVsLnNjb3Jlcwpsb2FkKHBhc3RlMChkcnVnX2Fzc2F5X3BhdGgsICIvbW9kZWwgc2NvcmVzLnJkYSIpKQojIHBzZXVkb19sb2cxMC5pbmRpdmlkdWFsLmxpbmVhci5tb2RlbApsb2FkKHBhc3RlMChkcnVnX2Fzc2F5X3BhdGgsICIvcHNldWRvX2xvZzEwIG1lZGlhbiBsaW5lYXIgbW9kZWxzLnJkYSIpKQojIHBzZXVkb19sb2cxMC5tZWRpYW4ubGluZWFyLm1vZGVsCmxvYWQocGFzdGUwKGRydWdfYXNzYXlfcGF0aCwgIi9wc2V1ZG9fbG9nMTAgaW5kaXZpZHVhbCBsaW5lYXIgbW9kZWxzLnJkYSIpKQojIE1lZGlhbkRvc2VyZXBvbnNlCmxvYWQocGFzdGUwKGRydWdfYXNzYXlfcGF0aCwgIi9tZWRpYW4gcGVuIGRvc2UgcmVzcG9uc2Ugd2l0aCBzY2FsZXMucmRhIikpCiMgQWxsUGVuRG9zZXJlcG9uc2UKbG9hZChwYXN0ZTAoZHJ1Z19hc3NheV9wYXRoLCAiL2FsbCBwZW4gZG9zZSByZXNwb25zZSB3aXRoIHNjYWxlcy5yZGEiKSkKYGBgCgojIyBTY29yZXMKCkhlcmUgd2Ugd2FudCB0byBjb21wYXJlIHRoZSBkcnVnIGVmZmVjdCBvbiBlYWNoIGZlYXR1cmUgdG8gdGhlICJub3JtYWwiIGJlaGF2aW9yIG9mIHRoZSBmZWF0dXJlIG1vZGVsZWQgaW4gT09MIHN0dWR5LgoKIyMjIEF1eGlsaWFyaWVzIHNjb3JlIGZ1bmN0aW9ucwoKKipkcnVnLmVmZmVjdCoqCgpgYGB7cn0KIyBGdW5jdGlvbiB3aGljaCByZXR1cm46CiMgKzEgZm9yIGFjdGl2YXRpbmcgZHJ1Z3MKIyAtMSBmb3IgaW5oaWJpdGluZyBkcnVncwojIDAgZm9yIG5vbiBhY3RpdmUgKG9yIHVuY2VydGFpbikgZHJ1Z3MKIyBiYXNlZCBvbiB0aGUgc2xvcGUgb2YgdGhlIG1vZGVsIGFuZCBpdHMgcC12YWx1ZQoKZHJ1Zy5lZmZlY3QgPC0gZnVuY3Rpb24oc2xvcGUsIHB2YWwsIHNsb3BlLnRocmVzaG9sZCwgcHZhbC50aHJlc2hvbGQpewogIGlmICgoYWJzKHNsb3BlKSA+IHNsb3BlLnRocmVzaG9sZCkgJiAocHZhbCA8IHB2YWwudGhyZXNob2xkKSAmICghaXMubmEoc2xvcGUpKSAmICghaXMubmEocHZhbCkpKXsKICAgIHJldHVybihzbG9wZS9hYnMoc2xvcGUpKQogIH0KICBlbHNlewogICAgcmV0dXJuKDApCiAgfQp9CmBgYAoKKipmZWF0dXJlLm5vcm1hbC5iZWhhdmlvci5hdC5UVEwqKgoKYGBge3J9CiMgRnVuY3Rpb24gd2hpY2ggcmV0dXJucwojIDEgaWYgdGhlIG1vZGVsIChvZiB0aGUgT09MIGZlYXR1cmUpIGlzIGluY3JlYXNpbmcgYXQgVFRMCiMgLTEgaWYgdGhlIG1vZGVsIChvZiB0aGUgT09MIGZlYXR1cmUpIGlzIGRlY3JlYXNpbmcgYXQgVFRMCiMgTkEgaWYgdGhlIGJlaGF2aW9yIG9mIHRoZSBmZWF0dXJlIGlzIGluY29uY2x1c2l2ZSAobWlzc2luZyBtb2RlbCBvciB0b28gd2VhayBzbG9wZSkKIyBiYXNlZCBvbiB0aGUgbW9kZWwgKGNvbnRhaW5lZCBpbiBhIGxpc3QpIGl0c2VsZiBhbmQgaXRzIHAtdmFsdWUKCmZlYXR1cmUubm9ybWFsLmJlaGF2aW9yLmF0LlRUTCA8LSBmdW5jdGlvbihtb2RlbCwgcHZhbCwgc2xvcGUudGhyZXNob2xkLCBwdmFsLnRocmVzaG9sZCwgVFRMKXsKICBpZiAobGVuZ3RoKHB2YWwpID09IDAgfHwgaXMubmEocHZhbCkgfHxwdmFsID4gcHZhbC50aHJlc2hvbGQgfHwgbGVuZ3RoKG1vZGVsKSA9PSAwKXsKICAgIHJldHVybihOQSkKICB9CiAgZWxzZXsKICAgICMgSGVyZSB3ZSBmb2N1cyB1biB0aGUgZGVyaXZhdGl2ZSBhcm91bmQgMCB0byBlc3RpbWF0ZSB0aGUgYmVoYXZpb3Igb2YgdGhlIGZlYXR1cmUKICAgIG1vZGVsIDwtIG1vZGVsW1sxXV0KICAgIHF1YWRyYXRpYyA8LSAobGVuZ3RoKG1vZGVsKSA9PSAzKQoKICAgICMgRGVyaXZhdGl2ZSBvZiB0aGUgbW9kZWwgYXQgVFRMCiAgICAjIGZvciBxdWFkcmF0aWMgbW9kZWwKICAgIGlmIChxdWFkcmF0aWMpewogICAgICBkZXJpdiA8LSAyKm1vZGVsW1snRE9TMiddXSpUVEwgKyBtb2RlbFtbJ0RPUyddXQogICAgfSAKICAgICMgZm9yIGxpbmVhciBtb2RlbAogICAgZWxzZXsKICAgICAgZGVyaXYgPC0gbW9kZWxbWydET1MnXV0KICAgIH0KICAgIAogICAgIyBSZXR1cm5pbmcgZWZmZWN0IGNvZWZmaWNpZW50CiAgICBpZiAoYWJzKGRlcml2KSA+IHNsb3BlLnRocmVzaG9sZCl7CiAgICAgIHJldHVybihkZXJpdi9hYnMoZGVyaXYpKQogICAgfQogICAgZWxzZXsKICAgICAgcmV0dXJuKE5BKQogICAgfQogIH0KfQpgYGAKCioqY3JlYXRlLmRydWcuZWZmZWN0LmRhdGEuZnJhbWUqKgoKYGBge3J9CiMgRnVuY3Rpb24gd2hpY2ggY3JlYXRlcyBhIGRhdGFmcmFtZSB3aXRoIAojIDEgd2hlbiB0aGUgZHJ1ZyBpcyBhZmZlY3RpbmcgdGhlIGZlYXR1cmUgdGhlIG9wcG9zaXRlIGRpcmVjdGlvbiBvZiBpdHMgbm9ybWFsIGJlaGF2aW9yIGF0IFRUTAojIC0xIHdoZW4gdGhlIGRydWcgaXMgYWZmZWN0aW5nIHRoZSBmZWF0dXJlIHRoZSBzYW1lIGRpcmVjdGlvbiBvZiBpdHMgbm9ybWFsIGJlaGF2aW9yIGF0IFRUTAojIDAgd2hlbiB0aGUgZWZmZWN0IG9mIHRoZSBkcnVnIGlzIG51bGwKIyBOQSB3aGVuIGJlaGF2aW9yIG9mIHRoZSBmZWF0dXJlIGlzIG51bGwgb3IgaGFyZCB0byBlc3RpbWF0ZSAoaGlnaCBwLXZhbHVlKSBvciBpZiB3ZSBtaXNzIGRhdGEgaW4gdGhlIGRydWcgYXNzYXkgc3R1ZHkKCmNyZWF0ZS5kcnVnLmVmZmVjdC5kYXRhLmZyYW1lIDwtIGZ1bmN0aW9uKGRydWcubmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsLmJlaGF2aW9yLnB2YWwudGhyZXNob2xkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsLmJlaGF2aW9yLnNsb3BlLnRocmVzaG9sZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRydWcucHZhbC50aHJlc2hvbGQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcnVnLnNsb3BlLnRocmVzaG9sZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRMLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcy5vZi5pbnRlcmVzdCl7CiAgCiAgIyBEYXRhZnJhbWUgZmVhdHVyZSB+IHN0aW11bGF0aW9uIGNvbnRhaW5pbmcgdGhlIGZpbmFsIGVmZmVjdCBvbiBhIGRydWcgb24gZWFjaCBmZWF0dXJlIHVuZGVyIGVhY2ggc3RpbXVsYXRpb24KICBkcnVnLmVmZmVjdC5kYXRhZnJhbWUgPC0gbW9kZWwuc2NvcmVzICU+JQogICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoZHJ1ZyA9PSBkcnVnLm5hbWUpICU+JQogICAgICAgICAgICAgICAgICAgICAgICByb3d3aXNlKCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShkcnVnLmJlaGF2aW9yID0gZHJ1Zy5lZmZlY3Qoc2xvcGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2xvcGUudGhyZXNob2xkID0gZHJ1Zy5zbG9wZS50aHJlc2hvbGQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsLnRocmVzaG9sZCA9IGRydWcucHZhbC50aHJlc2hvbGQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbiA9IHN0cl9leHRyYWN0KGZlYXR1cmUsICJeW15fXSsiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWdlbnRfc3RpbSA9IHN0cl9leHRyYWN0KGZlYXR1cmUsICIoPzw9XykuKiQiKSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChwb3B1bGF0aW9uLCBkcnVnLmJlaGF2aW9yLCByZWFnZW50X3N0aW0pICU+JQogICAgICAgICAgICAgICAgICAgICAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gcmVhZ2VudF9zdGltLCB2YWx1ZXNfZnJvbSA9IGRydWcuYmVoYXZpb3IpCiAgCiAgIyBDb25jYXRlbmF0aW9uIG9mIGFsbCBpbmZvcm1hdGlvbiBvbiB0aGUgZmVhdHVyZSBtb2RlbHMgKyBub3JtYWwuYmVoYXZpb3IgPSArMSwgLTEgb3IgTkEKICBtb2RlbC5pbmZvIDwtIG1lcmdlKGRmX21vZGVscywgY3VydmUuY2xhc3NpZmljYXRpb24sIGJ5LnggPSAiZmVhdHVyZSIsIGJ5LnkgPSAiY3l0b2YiKSAlPiUKICAgICAgICAgICAgICAgIHJvd3dpc2UoKSAlPiUKICAgICAgICAgICAgICAgIG11dGF0ZShub3JtYWwuYmVoYXZpb3IgPSBmZWF0dXJlLm5vcm1hbC5iZWhhdmlvci5hdC5UVEwobW9kZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWwuYmVoYXZpb3Iuc2xvcGUudGhyZXNob2xkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWwuYmVoYXZpb3IucHZhbC50aHJlc2hvbGQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUTCksCiAgICAgICAgICAgICAgICAgICAgICAgIyBBZGRpbmcgc29tZSBudWFuY2UgdG8gdGhlIGltcG9ydGFuY2Ugb2YgdGhlIGJlaGF2aW9yCiAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsLmJlaGF2aW9yID0gaWZlbHNlKGZlYXR1cmUgJWluJSBmZWF0dXJlcy5vZi5pbnRlcmVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsLmJlaGF2aW9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWwuYmVoYXZpb3IvMikpCgogICMgRGF0YWZyYW1lIGZlYXR1cmUgfiBzdGltdWxhdGlvbiBjb250YWluaW5nIHRoZSB2YWx1ZXMgb2Ygbm9ybWFsLmJlaGF2aW9yCiAgYmVoYXZpb3IuZGF0YWZyYW1lIDwtIG1vZGVsLmluZm8gJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKHBvcHVsYXRpb24gPSBzdHJfZXh0cmFjdChmZWF0dXJlLCAiXlteX10rIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWdlbnRfc3RpbSA9IHN0cl9leHRyYWN0KGZlYXR1cmUsICIoPzw9XykuKiQiKSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHBvcHVsYXRpb24sIG5vcm1hbC5iZWhhdmlvciwgcmVhZ2VudF9zdGltKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gcmVhZ2VudF9zdGltLCB2YWx1ZXNfZnJvbSA9IG5vcm1hbC5iZWhhdmlvcikgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyByZW1vdmluZyBmZWF0dXJlcyBub3QgbWVhc3VyZWQgaW4gYm90aCBzdHVkaWVzIChkcnVncyBhbmQgT09MKQogICAgICAgICAgICAgICAgICAgICAgICAgICMgYFNUQVQzX0xQU2AsIGBTVEFUNV9MUFNgLCBgU1RBVDFfTFBTYCwgYFNUQVQ2X0xQU2AsIGBTVEFUMV9HTUNTRmAKICAgICAgICAgICAgICAgICAgICAgICAgICAjICJTVEFUMV91bnN0aW0iCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHBvcHVsYXRpb24gJWluJSBkcnVnLmVmZmVjdC5kYXRhZnJhbWUkcG9wdWxhdGlvbikgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KG9uZV9vZihjb2xuYW1lcyhkcnVnLmVmZmVjdC5kYXRhZnJhbWUpKSkKICAgICAgICAgICAgICAgICAgICAgICAgCiAgIyBEYXRhZnJhbWUgZmVhdHVyZSB+IHN0aW11bGF0aW9uIGNvbnRhaW5pbmcgdGhlIHByb2R1Y3Qgb2YgYmVoYXZpb3IubWF0cml4IGFuZCBkcnVnLmVmZmVjdC5tYXRyaXgKICBkcnVnLmZpbmFsLmVmZmVjdC5kYXRhZnJhbWU8LSBtZXJnZShwaXZvdF9sb25nZXIoc2VsZWN0KGJlaGF2aW9yLmRhdGFmcmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25lX29mKGNvbG5hbWVzKGRydWcuZWZmZWN0LmRhdGFmcmFtZSkpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHMgPSAtcG9wdWxhdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInJlYWdlbnRfc3RpbSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAibm9ybWFsLmJlaGF2aW9yIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGl2b3RfbG9uZ2VyKHNlbGVjdChkcnVnLmVmZmVjdC5kYXRhZnJhbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmVfb2YoY29sbmFtZXMoYmVoYXZpb3IuZGF0YWZyYW1lKSkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29scyA9IC1wb3B1bGF0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAicmVhZ2VudF9zdGltIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJkcnVnLmJlaGF2aW9yIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJwb3B1bGF0aW9uIiwgInJlYWdlbnRfc3RpbSIpKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3d3aXNlKCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGRydWcuZmluYWwuZWZmZWN0ID0gaWZlbHNlKGlzLm5hKG5vcm1hbC5iZWhhdmlvcikgfHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpcy5uYShkcnVnLmJlaGF2aW9yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1ub3JtYWwuYmVoYXZpb3IqZHJ1Zy5iZWhhdmlvcikpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChwb3B1bGF0aW9uLCByZWFnZW50X3N0aW0sIGRydWcuZmluYWwuZWZmZWN0KSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gcmVhZ2VudF9zdGltLCB2YWx1ZXNfZnJvbSA9IGRydWcuZmluYWwuZWZmZWN0KQoKICByZXR1cm4oZHJ1Zy5maW5hbC5lZmZlY3QuZGF0YWZyYW1lKQp9CmBgYAoKIyMjIEZlYXR1cmVzIG9mIGludGVyZXN0CgpGb3Igbm93LCB3ZSBkb24ndCBoYXZlIGEgTUwgbW9kZWwgd2l0aCBzZWxlY3RlZCBmZWF0dXJlcyBoZWxwaW5nIHVzIGRpc2NyaW1pbmF0ZSB3aGljaCBmZWF0dXJlcyBhcmUgbW9yZSBpbXBvcnRhbnQgdG8gbG9vayBhdCBzbyB3ZSBhcmUgbG9va2luZyBhdCB0d28gc2V0cyBvZiBmZWF0dXJlcyA6Ci0gdGhlIHRvcCAxNSBDeXRvZiBmZWF0dXJlcyBmcm9tIHRoZSBPT0wgcGFwZXIKLSB0aGUgZmVhdHVyZXMgZnJvbSB0aGUgdW5pdmFyaWF0ZSBhbmFseXNpcyB3aXRoIGFuIGFic29sdXRlIFNwZWFybWFuIHNjb3JlID4gMC4zCgoqKlRvcCAxNSBmZWF0dXJlcyBmcm9tIE9PTCBwYXBlcioqCgpgYGB7cn0KIyBUb3AgaW1tdW5vbWUgZmVhdHVyZXMgZnJvbSB0aGUgT09MIHN0dWR5CiMgd2UgYXNzb2NpYXRlIGEgY29lZmYgKzEgKGluY3JlYXNpbmcpIG9yIC0xIChkZWNyZWFzaW5nKSB0byB0aGUgZmVhdHVyZXMKT09MLnRvcC5pbW11bm9tZS5mZWF0dXJlcyA8LSBsaXN0KCJDRDU2bG9DRDE2cG9zTktfU1RBVDFfSUZOYSI9MSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgR3JhbnVsb2N5dGVzIG1pc3NpbmcgaW4gdGhlIGRydWcgYXNzYXkgc3R1ZHkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDRDU2aGlDRDE2bmVnTktfU1RBVDFfSUZOYSI9MSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDRDRUbmFpdmVfTUFQS0FQSzJfSUZOYSI9MSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuY01Dc19DUkVCX0dNQ1NGIj0tMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDRDhUY21fTUFQS0FQSzJfdW5zdGltIj0xLCAjLTEgYXQgVFRMID0gMAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNEOFRlbV9NQVBLQVBLMl91bnN0aW0iPTEsICMtMSBhdCBUVEwgPSAwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicERDc19TVEFUMV9JRk5hIj0xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJjZWxsc19NQVBLQVBLMl9MUFMiPTEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0Q0VGVtX01BUEtBUEsyX3Vuc3RpbSI9MSwgIy0xIGF0IFRUTCA9IDAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDRDhUY21fTUFQS0FQSzJfSUZOYSI9MSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0Q4VGVtX01BUEtBUEsyX0lGTmEiPTEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEJjZWxscyBtaXNzaW5nIGluIHRoZSBkcnVnIGFzc2F5IHN0dWR5CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0Q0VGVtX05Ga0JfSUwyNDYiPS0xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNENFRjbV9Ja0JfdW5zdGltIj0xLCAjLTEgYXQgVFRMID0gMAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm1EQ3NfU1RBVDZfSUZOYSI9MSwgIy0xIGF0IFRUTCA9IDAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwRENzX1NUQVQ2X0lGTmEiPTEsICMtMSBhdCBUVEwgPSAwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibURDc19NQVBLQVBLMl91bnN0aW0iPS0xLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwRENzX01BUEtBUEsyX3Vuc3RpbSI9LTEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKYGBgCgoqKlRvcCBmZWF0dXJlcyBmcm9tIHRoZSB1bml2YXJpYXRlIGFuYWx5c2lzKioKCmBgYHtyfQojIFRvcCBpbW11bm9tZSBmZWF0dXJlcyBmcm9tIHRoZSB1bml2YXJpYXRlIGFuYWx5c2lzIG9uIE9PTCBkYXRhCiMgd2UgYXNzb2NpYXRlIGEgY29lZmYgKzEgKGluY3JlYXNpbmcpIG9yIC0xIChkZWNyZWFzaW5nKSB0byB0aGUgZmVhdHVyZXMKCnVuaXZhcmlhdGUuT09MLmZlYXR1cmVzIDwtIGFwcGx5KHNwZWFybWFuX2NvclthYnMoc3BlYXJtYW5fY29yJGBTcGVhcm1hbiBjb3JyYCkgPiAwLjMsXSwgIyB0YWtpbmcgZmVhdHVyZXMgd2l0aCB8U3BlYXJtYW5yfCA+IDAuMwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihyb3cpeyAjIGFzc29jaWF0aW5nIHRoZSBmZWF0dXJlIG5hbWUgdG8gKzEgKGluY3JlYXNpbmcpIG9yIC0xIChkZWNyZWFzaW5nKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleSA8LSByb3dbWzFdXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICghaXMubmEoa2V5KSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPC0gYXMubnVtZXJpYyhyb3dbWzJdXSkvYWJzKGFzLm51bWVyaWMocm93W1syXV0pKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2V0TmFtZXMobGlzdCh2YWx1ZSksIGtleSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGaWx0ZXIoZnVuY3Rpb24oeCkgIWlzLm51bGwoeCksIC4pICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVubGlzdChyZWN1cnNpdmU9RkFMU0UpCmBgYAoKKipUb3AgZmVhdHVyZXMgZnJvbSBmZWF0dXJlLmluZGV4KioKCmBgYHtyfQojIFRvcCBpbW11bm9tZSBmZWF0dXJlcyBmcm9tIHRoZSBmZWF0dXJlLmluZGV4CiMgd2UgYXNzb2NpYXRlIGEgY29lZmYgKzEgKGluY3JlYXNpbmcpIG9yIC0xIChkZWNyZWFzaW5nKSB0byB0aGUgZmVhdHVyZXMKCiMgVGltZXBvaW50IG9mIGludGVyZXN0ClRUTCA8LSAtODQKCiMgVGhyZXNob2xkcyB0byBjYWxjdWxhdGUgdGhlIG5vcm1hbCBiZWhhdmlvciBvZiBhIGdpdmVuIGZlYXR1cmUKbm9ybWFsLmJlaGF2aW9yLnNsb3BlLnRocmVzaG9sZCA8LSAwLgpub3JtYWwuYmVoYXZpb3IucHZhbC50aHJlc2hvbGQgPC0gMTAKCiMgSW5mbyAobW9kZWwsIHB2YWwsIHJtc2UsIC4uLikgb2YgdGhlIG1vZGVscyBhdCB0aGUgdGltZXBvaW50IFRUTAptb2RlbC5pbmZvIDwtIG1lcmdlKGRmX21vZGVscywgY3VydmUuY2xhc3NpZmljYXRpb24sIGJ5LnggPSAiZmVhdHVyZSIsIGJ5LnkgPSAiY3l0b2YiKSAlPiUKICAgICAgICAgICAgICByb3d3aXNlKCkgJT4lCiAgICAgICAgICAgICAgbXV0YXRlKG5vcm1hbC5iZWhhdmlvciA9IGZlYXR1cmUubm9ybWFsLmJlaGF2aW9yLmF0LlRUTChtb2RlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsLmJlaGF2aW9yLnNsb3BlLnRocmVzaG9sZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbC5iZWhhdmlvci5wdmFsLnRocmVzaG9sZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRMKSkKIyBMaXN0IG9mIHRoZSBmZWF0dXJlcyBhbmQgdGhlaXIgYmVoYXZpb3IKdG9wLmZlYXR1cmUuaW5kZXggPC0gZmVhdHVyZS5pbmRleCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKG1vZGVsX2luZGV4ID4gMCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGxlZnRfam9pbihtb2RlbC5pbmZvLCBieT0iZmVhdHVyZSIpICU+JQogICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoZmVhdHVyZSwgbm9ybWFsLmJlaGF2aW9yKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgcHVsbChub3JtYWwuYmVoYXZpb3IpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgc2V0TmFtZXMocHVsbChmZWF0dXJlLmluZGV4WyhmZWF0dXJlLmluZGV4JG1vZGVsX2luZGV4ID4gMCkgJiAhKGlzLm5hKGZlYXR1cmUuaW5kZXgkbW9kZWxfaW5kZXgpKSxdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlKSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGFzLmxpc3QoKQpgYGAKCiMjIyBEcnVncyByYW5raW5nCgpIZXJlIHdlIHJhbmsgdGhlIGRydWdzIGJhc2VkIG9uIHRoZWlyIGVmZmVjdCBvbiB0aGUgY2hvc2VuIGZlYXR1cmVzIG9mIGludGVyZXN0LiBXZSBhc3NvY2lhdGUgYSArMSBzY29yZSB3aGVuIHRoZSBkcnVnIGhhcyBhbiBlZmZlY3Qgd2hpY2ggaXMgb3Bwb3NpdGUgdG8gdGhlICJub3JtYWwiIGJlaGF2aW9yLyB0cmVuZCBvZiB0aGUgZmVhdHVyZSBhbmQgLTEgd2hlbiB0aGUgZWZmZWN0IG9mIHRoZSBkcnVnIGlzIGNvbGluZWFyIHRvIHRoZSAibm9ybWFsIiBmZWF0dXJlIGJlaGF2aW9yLgoKYGBge3J9CiMgTW9kZWxzIG9mIGludGVyZXN0CmZpbmFsLm1vZGVsIDwtIHBzZXVkb19sb2cxMC5tZWRpYW4ubGluZWFyLm1vZGVsCmZpbmFsLm1vZGVsLnR5cGUgPC0gIm1lZGlhbiIgIyJpbmRpdmlkdWFsIiBvciAibWl4ZWQiIG9yICJtZWRpYW4iCgojIENob3NpbmcgZmVhdHVyZXMgb2YgaW50ZXJlc3QKZmVhdHVyZV9jaG9pY2UgPC0gInRvcF9mZWF0dXJlX2luZGV4X2ZlYXR1cmVzIgpPT0xfZmVhdHVyZXMgPC0gdG9wLmZlYXR1cmUuaW5kZXgKCiMgQ2hvc2luZyB0aHJlc2hvbGRzIG92ZXIgb3IgdW5kZXIgd2hpY2ggd2UgY29uc2lkZXIgdGhlIGVmZmVjdCBvZiB0aGUgZHJ1ZyBhcyByZWxldmFudApzbG9wZS50aHJlc2hvbGQgPC0gMApwdmFsLnRocmVzaG9sZCA8LSAxCmBgYAoKQ2FsY3VsYXRpb24gb2YgdGhlIHNjb3JlcyBhbmQgcmFua2luZyBvZiB0aGUgZmVhdHVyZXMuCgoqKmRydWcucmFua2luZyA6KiogZGF0YWZyYW1lIGNvbnRhaW5pbmcgdGhlIG5hbWUgb2YgdGhlIGRydWdzIGFuZCB0aGVpciBzY29yZS4KCmBgYHtyfQpkcnVnLnJhbmtpbmcgPC0gZGF0YS5mcmFtZShkcnVnID0gY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjb3JlID0gbnVtZXJpYygpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgojIEdldHRpbmcgYSBub3JtaXplZCBsb2cgdHJhbnNmb3JtIG9mIHRoZSBmZWF0dXJlIGluZGV4IHRvIGJlIHVzZWQgYXMgY29lZmZpY2llbnQgaW4gdGhlIHNjb3JlCm5vcm0ubG9nLmZlYXR1cmUuaW5kZXggPC0gZmVhdHVyZS5pbmRleCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShsb2cubW9kZWxfaW5kZXggPSBsb2cxMCgxICsgbW9kZWxfaW5kZXgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm0ubG9nLm1vZGVsX2luZGV4ID0gbG9nLm1vZGVsX2luZGV4L3N1bShsb2cubW9kZWxfaW5kZXgsIG5hLnJtPVRSVUUpKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChmZWF0dXJlLCBub3JtLmxvZy5tb2RlbF9pbmRleCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIHB1bGwobm9ybS5sb2cubW9kZWxfaW5kZXgpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgc2V0TmFtZXMocHVsbChmZWF0dXJlLmluZGV4LCBmZWF0dXJlKSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGFzLmxpc3QoKQoKIyBDYWxjdWxhdGluZyB0aGUgc2NvcmUKZm9yIChkcnVnIGluIHVuaXF1ZShmaW5hbC5tb2RlbCRkcnVnKSl7CiAgc2NvcmUgPC0gMAogICMgV2Ugc3VtIHRoZSBzY29yZXMgb3ZlciBhbGwgdGhlIGZlYXR1cmVzIGZvciBlYWNoIGRydWcKICBmb3IgKGZlYXR1cmUgaW4gbmFtZXMoT09MX2ZlYXR1cmVzKSl7CiAgICAjIEdldHRpbmcgdGhlIGRydWcgZWZmZWN0IG9uIHRoZSBmZWF0dXJlCiAgICBzbG9wZSA8LSBtb2RlbC5zY29yZXNbKG1vZGVsLnNjb3JlcyRmZWF0dXJlID09IGZlYXR1cmUpICYgKG1vZGVsLnNjb3JlcyRkcnVnID09IGRydWcpLF0kc2xvcGUKICAgIHB2YWwgPC0gbW9kZWwuc2NvcmVzWyhtb2RlbC5zY29yZXMkZmVhdHVyZSA9PSBmZWF0dXJlKSAmIChtb2RlbC5zY29yZXMkZHJ1ZyA9PSBkcnVnKSxdJHB2YWwKICAgICMgU29tZXRpbWVzIHRoZSB2YWx1ZXMgb2Ygc2xvcGUgYW5kL29yIHB2YWwgaXMgZW1wdHkgc28gd2UgZmlsbCB3aXRoIDAgdGhlc2UgdmFsdWVzCiAgICBkcnVnX2VmZmVjdCA8LSBpZmVsc2UoKGxlbmd0aChzbG9wZSkqbGVuZ3RoKHB2YWwpID09IDApIHx8IChpcy5uYShzbG9wZSkgfCBpcy5uYShwdmFsKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgMCwKICAgICAgICAgICAgICAgICAgICAgICAgICBkcnVnLmVmZmVjdChzbG9wZSwgcHZhbCwgc2xvcGUudGhyZXNob2xkLCBwdmFsLnRocmVzaG9sZCkpCiAgICAjIEFkZGluZyB0aGUgaW5kaXZpZHVhbCBzY29yZXMgb2YgdGhlIGRydWcgZWFjaCBmZWF0dXJlIAogICAgc2NvcmUgPC0gc2NvcmUgLSBkcnVnX2VmZmVjdCpPT0xfZmVhdHVyZXNbW2ZlYXR1cmVdXSpub3JtLmxvZy5mZWF0dXJlLmluZGV4W1tmZWF0dXJlXV0KICB9CiAgIyBBZGRpbmcgdGhlIGRydWcgYW5kIGl0cyBzY29yZSB0byBkcnVnLnJhbmtpbmcKICBkcnVnLnJhbmtpbmcgPC0gcmJpbmQoZHJ1Zy5yYW5raW5nLAogICAgICAgICAgICAgICAgICAgICAgICBkYXRhLmZyYW1lKGRydWc9ZHJ1ZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY29yZT1zY29yZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpKQp9CiMgQXJyYW5naW5nIHRoZSBkcnVncyB0byBzZWUgdGhlIGJlc3QgZHJ1Z3Mgb24gdG9wCmRydWcucmFua2luZyA8LSBkcnVnLnJhbmtpbmcgJT4lIGFycmFuZ2UoZGVzYyhzY29yZSkpCmRydWcucmFua2luZwpgYGAKCiMjIFZpc3VhbGl6YXRpb24KCiMjIyBNb2RlbHMgdmVyc3VzICJub3JtYWwiIGJlaGF2aW9yCgpBdXhpbGlhcnkgZnVuY3Rpb24gdG8gcGxvdCB0aGUgbWl4ZWQgbW9kZWwuCgpgYGB7cn0KIyBGdW5jdGlvbiB0YWtpbmcgdGhlIG5hbWUgb2YgYSBmZWF0dXJlLCBuYW1lIG9mIGEgZHJ1ZyBhbmQgYSBwdmFsdWUKIyBSZXR1cm5pbmcgYSBwbG90IG9mIHRoZSBtb2RlbCBjb3JyZXNwb25kaW5nIHRvIHRoZSBmZWF0dXJlIGFuZCBkcnVnCgpwbG90X2RydWdfZmVhdHVyZV9tb2RlbCA8LSBmdW5jdGlvbih0YXJnZXRfZHJ1ZywgdGFyZ2V0X2ZlYXR1cmUsIHBfdmFsdWUpewogICMgR2V0dGluZyB0aGUgbW9kZWwgYXNzb2NpYXRlZCB0byB0aGUgdGFyZ2V0X2RydWcgYW5kIHRoZSB0YXJnZXRfZmVhdHVyZQogIHRhcmdldF9tb2RlbCA8LSBmaW5hbC5tb2RlbCRtb2RlbFtmaW5hbC5tb2RlbCRmZWF0dXJlID09IHRhcmdldF9mZWF0dXJlICYgZmluYWwubW9kZWwkZHJ1ZyA9PSB0YXJnZXRfZHJ1Z11bWzFdXQogICMgR2V0dGluZyB0aGUgcHZhbCBhc3NvY2lhdGVkIHdpdGggdGhlIHRhcmdldF9tb2RlbAogIHB2YWwgPC0gZmluYWwubW9kZWwkcHZhbFtmaW5hbC5tb2RlbCRmZWF0dXJlID09IHRhcmdldF9mZWF0dXJlICYgZmluYWwubW9kZWwkZHJ1ZyA9PSB0YXJnZXRfZHJ1Z11bWzFdXQogICMgU2V0dGluZyB0aGUgZG9zZSB2YWx1ZXMgdG8gdGhlaXIgcHNldWRvIGxvZyB0cmFuc2Zvcm0KICBEb3NlcmVzcG9uc2UgPC0gQWxsUGVuRG9zZXJlc3BvbnNlICU+JQogICAgICAgICAgICAgICAgICAgIG11dGF0ZShkb3NlID0gcHNldWRvX2xvZ19kb3NlLAogICAgICAgICAgICAgICAgICAgICAgICAgICBJRCA9IGFzLmZhY3RvcihJRCkpICU+JQogICAgICAgICAgICAgICAgICAgIGZpbHRlcihpcy5maW5pdGUoZG9zZSkpCiAgIyBTdWJzZXQgb2YgdGhlIGRhdGEgZm9yIHRoZSBjaG9zZW4gZmVhdHVyZSBhbmQgZHJ1ZwogIGRhdGEgPC0gRG9zZXJlc3BvbnNlICU+JSAKICAgICAgICAgICAgICBmaWx0ZXIoZmVhdHVyZSA9PSAhIXRhcmdldF9mZWF0dXJlLAogICAgICAgICAgICAgICAgICAgICBkcnVnID09ICEhdGFyZ2V0X2RydWcpICU+JQogICAgICAgICAgICAgIHNlbGVjdChJRCwgZG9zZSwgdmFsdWUpCiAgICAKICAjIyMgUExPVCBUSEUgTUVESUFOIE1PREVMCiAgaWYgKGZpbmFsLm1vZGVsLnR5cGUgPT0gIm1lZGlhbiIpewogICAgIyBDb2VmZmljaWVudHMgb2YgdGhlIHNlbGVjdGVkIG1vZGVsCiAgICBtb2RlbF9jb2VmcyA8LSBjb2VmKHRhcmdldF9tb2RlbCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICB0KCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICBgY29sbmFtZXM8LWAoYygiSW50ZXJjZXB0IiwgIlNsb3BlIikpCiAgICAKICAgIGRhdGFfcmFuaSA8LSBtZXJnZShkYXRhLCBtb2RlbF9jb2VmcykKICAgIAogICAgIyBQbG90CiAgICBwbG90IDwtIGdncGxvdChkYXRhX3JhbmksIGFlcyh4PWRvc2UsIHk9dmFsdWUsIGdyb3VwPWRvc2UpKSArCiAgICAgICAgICAgICAgZ2VvbV9ib3hwbG90KCkgKwogICAgICAgICAgICAgIGdlb21fYWJsaW5lKGFlcyhpbnRlcmNlcHQgPSBJbnRlcmNlcHQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzbG9wZSA9IFNsb3BlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMS4pICsKICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKICB9CiAgCiAgIyMjIFBMT1QgVEhFIElORElWSURVQUwgTU9ERUwKICBpZiAoZmluYWwubW9kZWwudHlwZSA9PSAiaW5kaXZpZHVhbCIpewogICAgIyBDb2VmZmljaWVudHMgb2YgdGhlIHNlbGVjdGVkIG1vZGVsCiAgICBtb2RlbF9jb2VmcyA8LSB0YXJnZXRfbW9kZWxzICU+JQogICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKFNsb3BlID0gc2FwcGx5KG1vZGVsLCBmdW5jdGlvbih4KSBjb2VmKHgpWyJkb3NlIl0pLAogICAgICAgICAgICAgICAgICAgICAgSW50ZXJjZXB0ID0gc2FwcGx5KG1vZGVsLCBmdW5jdGlvbih4KSBjb2VmKHgpWyIoSW50ZXJjZXB0KSJdKSwKICAgICAgICAgICAgICAgICAgICAgIElEID0gYXMuZmFjdG9yKElEKSkKICAgIAogICAgZGF0YV9yYW5pIDwtIGxlZnRfam9pbihkYXRhLCBtb2RlbF9jb2VmcywgYnkgPSAiSUQiKQogICAgCiAgICAjIFBsb3QKICAgIHBsb3QgPC0gZ2dwbG90KGRhdGEgPSBkYXRhX3JhbmksIG1hcHBpbmcgPSBhZXMoeCA9IGRvc2UsIHkgPSB2YWx1ZSwgY29sb3VyID0gSUQpKSArCiAgICAgICAgICAgICAgZ2VvbV9wb2ludChuYS5ybSA9IFQsIGFscGhhID0gMC41KSArCiAgICAgICAgICAgICAgZ2VvbV9hYmxpbmUoYWVzKGludGVyY2VwdCA9IEludGVyY2VwdCwgc2xvcGUgPSBTbG9wZSwgY29sb3VyID0gSUQpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMS4pICsKICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKICB9CiAgCiAgIyMjIFBMT1QgVEhFIE1JWEVEIExJTkVBUiBNT0RFTAogIGlmIChmaW5hbC5tb2RlbC50eXBlID09ICJtaXhlZCIpewogICAgIyBDb2VmZmljaWVudHMgb2YgdGhlIHNlbGVjdGVkIG1vZGVsCiAgICBtb2RlbF9jb2VmcyA8LSBjb2VmKHRhcmdldF9tb2RlbCkkSUQgJT4lIAogICAgICAgICAgICAgICAgICAgICAgcmVuYW1lKEludGVyY2VwdCA9IGAoSW50ZXJjZXB0KWAsIFNsb3BlID0gZG9zZSkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKCJJRCIpCiAgICAKICAgIGRhdGFfcmFuaSA8LSBsZWZ0X2pvaW4oZGF0YSwgbW9kZWxfY29lZnMsIGJ5ID0gIklEIikKICAgIAogICAgIyBQbG90CiAgICBwbG90IDwtIHN1cHByZXNzV2FybmluZ3MoZ2dwbG90KGRhdGEgPSBkYXRhX3JhbmksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBkb3NlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gdmFsdWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IElEKSkgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV9wb2ludChuYS5ybSA9IFQsIGFscGhhID0gMC41KSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW9tX2FibGluZShhZXMoaW50ZXJjZXB0ID0gSW50ZXJjZXB0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzbG9wZSA9IFNsb3BlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IElEKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxLikpCiAgfQogIAogICMgUmVtb3ZpbmcgdW5lY2Vzc2FyeSBsYWJlbHMgYW5kIGFkZGluZyB0aXRsZQogIHBsb3QgPC0gcGxvdCArIAogICAgICBnZ3RpdGxlKHBhc3RlKHRhcmdldF9mZWF0dXJlLCBwdmFsLCBzZXA9IixcbnAgPSAiKSkgKwogICAgICB4bGFiKE5VTEwpICsgICMgUmVtb3ZlIHgtYXhpcyBsYWJlbAogICAgICB5bGFiKE5VTEwpICsgICMgUmVtb3ZlIHktYXhpcyBsYWJlbAogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNikpCiAgCiAgcmV0dXJuKHBsb3QpCn0KYGBgCgpQbG90dGluZyB0aGUgaW5kaXZpZHVhbCBtb2RlbHMgb2YgdGhlIGRydWcgb24gdGhlIGNob3NlbiBmZWF0dXJlcyB2ZXJzdXMgdGhlIG5vcm1hbCBiZWhhdmlvciBvZiB0aGUgT09MIGZlYXR1cmUgaW4gb3JkZXIgdG8gc2VlIGlmIHRoZSB0cmVuZHMgYXJlIGFjdHVhbGx5IG9wcG9zaXRlcyAob3IgY29saW5lYXIpIGFuZCB0aGF0IHRoZSBtb2RlbHMgZml0IHRoZSBkYXRhLgoKYGBge3J9CiMgU2VsZWN0aW5nIGEgc3BlY2lmaWMgZHJ1ZyBhbmQgcGxvdHRpbmcgdGhlIGNvbXBhcmlzb24gb2YgdGhlIG1vZGVsIG9mIHRoZSBmZWF0dXJlcyB3aXRoIHRoZSBtaXhlZCBtb2RlbCBvZiB0aGUgZHJ1Zwp0YXJnZXRfZHJ1ZyA8LSAiTWV0Zm9ybWluIgojIFRpbWUgdG8gbGFib3Igb2YgaW50ZXJlc3QgKHdpbGwgcGxvdCB0aGUgZGVyaXZhdGl2ZSBhdCB0aGlzIHBvaW50KQpUVEwgPC0gLTg0CgpwbG90cyA8LSBsaXN0KCkKaSA8LSAwCmZvciAodGFyZ2V0X2ZlYXR1cmUgaW4gbmFtZXMoT09MX2ZlYXR1cmVzKSkgewogIHRhcmdldF9tb2RlbCA8LSBmaW5hbC5tb2RlbCRtb2RlbFtmaW5hbC5tb2RlbCRmZWF0dXJlID09IHRhcmdldF9mZWF0dXJlICYgZmluYWwubW9kZWwkZHJ1ZyA9PSB0YXJnZXRfZHJ1Z10KICBwdmFsIDwtIG1vZGVsLnNjb3Jlc1sobW9kZWwuc2NvcmVzJGZlYXR1cmUgPT0gdGFyZ2V0X2ZlYXR1cmUpICYgKG1vZGVsLnNjb3JlcyRkcnVnID09IHRhcmdldF9kcnVnKSxdJHB2YWwKICAKICAjIFdlIG9ubHkgcGxvdCBmZWF0dXJlcyBvbiB3aGljaCB0aGUgc2NvcmUgaGFzIGJlZW4gY2FsY3VsYXRlZCAod2l0aCBtaXhlZCBtb2RlbCBoYXZpbmcgYSBwdmFsdWUgPCBwdmFsIDwgcHZhbC50aHJlc2hvbGQpCiAgaWYgKChsZW5ndGgocHZhbCkgPiAwKSAmJgogICAgICAoIWlzLm5hKHB2YWwpKSAmJgogICAgICAocHZhbCA8IHB2YWwudGhyZXNob2xkKSAmJiAKICAgICAgKGxlbmd0aCh0YXJnZXRfbW9kZWwpPjApKXsKICAgICMgQ291bnRpbmcgdGhlIG51bWJlciBvZiBwbG90cwogICAgaSA8LSBpICsgMgogICAgCiAgICAjIyMgUExPVCBUSEUgSU5ESVZJRFVBTCBMSU5FQVIgTU9ERUwKICAgIG1vZGVsX2NvZWZfcGxvdCA8LSBwbG90X2RydWdfZmVhdHVyZV9tb2RlbCh0YXJnZXRfZHJ1ZywgdGFyZ2V0X2ZlYXR1cmUsIHB2YWwpCiAgICAjIEFkZGluZyBwbG90IHRvIHRoZSBsaXN0IG9mIHBsb3RzCiAgICBwbG90c1tbdGFyZ2V0X2ZlYXR1cmVdXSA8LSBtb2RlbF9jb2VmX3Bsb3QKCiAgICAKICAgICMjIyBQTE9UIFRIRSBBU1NPQ0lBVEVEIE9PTCBGRUFUVVJFCiAgICAjIEdldHRpbmcgdGhlIGNvZWZmaWNpZW50cyBhbmQgY2xhc3Mgb2YgdGhlIG1vZGVsCiAgICBjb2VmcyA8LSBkZl9tb2RlbHNbZGZfbW9kZWxzJGZlYXR1cmUgPT0gdGFyZ2V0X2ZlYXR1cmUsXSRtb2RlbFtbMV1dW1sxXV0KICAgIAogICAgIyBCdWlsZGluZyB0aGUgcHJlZGljdGlvbnMKICAgIHkgPC0gT09MX2RhdGFbLCB0YXJnZXRfZmVhdHVyZV0KICAgIGludGVyY2VwdCA8LSBjb2Vmc1tbJyhJbnRlcmNlcHQpJ11dCiAgICBpZiAobGVuZ3RoKGNvZWZzKSA9PSAzKXsgIyBxdWFkcmF0aWMgbW9kZWwKICAKICAgICAgYSA8LSBjb2Vmc1tbJ0RPUzInXV0KICAgICAgYiA8LSBjb2Vmc1tbJ0RPUyddXQogICAgICB5X3ByZWQgPC0gaW50ZXJjZXB0ICsgYiAqIERPUyArIGEgKiBET1NeMgogICAgICB5X2Rlcml2IDwtICgyKmEqVFRMICsgYikqKERPUyAtIFRUTCkgKyAoaW50ZXJjZXB0ICsgYiAqIFRUTCArIGEgKiBUVExeMikKICAgIH0KICAgIGVsc2V7ICMgbGVuZ3RoKGNvZWZzKSA9PSAyLCBsaW5lYXIgbW9kZWwKICAgICAgc2xvcGUgPC0gY29lZnNbWydET1MnXV0KICAgICAgeV9wcmVkIDwtIGludGVyY2VwdCArIHNsb3BlICogRE9TCiAgICAgIHlfZGVyaXYgPC0gc2xvcGUqKERPUyAtIFRUTCkgKyAoaW50ZXJjZXB0ICsgc2xvcGUgKiBUVEwpCiAgICB9CiAgICBkZiA8LSBkYXRhLmZyYW1lKHggPSBET1MsIHkgPSB5LCB5X3ByZWQgPSB5X3ByZWQsIHlfZGVyaXYgPSB5X2Rlcml2KQogICAgCiAgICAjIEdldHRpbmcgcHZhbHVlIG9mIHRoZSBtb2RlbAogICAgcHZhbCA8LSBjdXJ2ZS5jbGFzc2lmaWNhdGlvbltjdXJ2ZS5jbGFzc2lmaWNhdGlvbiRjeXRvZiA9PSB0YXJnZXRfZmVhdHVyZSxdJHB2YWwKICAgICMgUGxvdAogICAgcGxvdCA8LSBnZ3Bsb3QoZGYsIGFlcyh4ID0geCwgeSA9IHkpKSArCiAgICAgIGdlb21fcG9pbnQoY29sb3IgPSAiYmx1ZSIsIGFscGhhID0gMC41LCBzaXplID0gMSkgKwogICAgICBnZW9tX2xpbmUoYWVzKHkgPSB5X3ByZWQpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMC43KSArCiAgICAgIGdlb21fbGluZShhZXMoeSA9IHlfZGVyaXYpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMC43KSArCiAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IFRUTCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDAuNykgKwogICAgICBnZ3RpdGxlKHBhc3RlKHRhcmdldF9mZWF0dXJlLCBwdmFsLCBzZXA9IixcbnAgPSAiKSkgKwogICAgICB4bGFiKE5VTEwpICsgICMgUmVtb3ZlIHgtYXhpcyBsYWJlbAogICAgICB5bGFiKE5VTEwpICsgICMgUmVtb3ZlIHktYXhpcyBsYWJlbAogICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSkKICAgICMgQWRkaW5nIHBsb3QgdG8gdGhlIGxpc3Qgb2YgcGxvdHMKICAgIHBsb3RzW1twYXN0ZTAodGFyZ2V0X2ZlYXR1cmUsIi5PT0wiKV1dIDwtIHBsb3QKICB9Cn0KCiMgU2F2aW5nIHRoZSBwbG90cwpwZGYocGFzdGUoZHJ1Z19hc3NheV9wYXRoLCAiL3Bsb3RzLyIsIHRhcmdldF9kcnVnLCAiX29uXyIsIGZlYXR1cmVfY2hvaWNlLCIucGRmIiwgc2VwID0gIiIpKQpmb3IgKGogaW4gMTpjZWlsaW5nKGkvMTYpKXsKICBncmlkLmFycmFuZ2UoZ3JvYnMgPSBwbG90c1soMTYqKGotMSkrMSk6bWluKDE2KmosaSldLCBucm93ID0gNCwgbmNvbCA9IDQpCn0KZGV2Lm9mZigpCmBgYAoKIyMjIEdsb2JhbCB2aXN1YWxpemF0aW9uIDogaGVhdG1hcAoKYGBge3J9CiMgRnVuY3Rpb24gdG8gcGxvdCB0aGUgcGhlYXRtYXAgb2YgdGhlIGZpbmFsIGRydWcgZWZmZWN0CnBsb3RfcGhlYXRtYXAgPC0gZnVuY3Rpb24oZHJ1Zy5uYW1lLCBkcnVnLmZpbmFsLmVmZmVjdC5kYXRhZnJhbWUsIGNsdXN0ZXJpbmdfcm93cywgY2x1c3RlcmluZ19jb2xzLCBkZW5kcm89RkFMU0UpewogIG15X2NvbG9ycyA8LSBjKCJibHVlIiwgcmdiKDAsIDAsIDEsIGFscGhhID0gMC41KSwgIndoaXRlIiwgcmdiKDEsIDAsIDAsIGFscGhhID0gMC41KSwgInJlZCIpCiAgCiAgIyBQcmVwYXJpbmcgdGhlIGRhdGEKICBkYXRhIDwtIGFzLm1hdHJpeChkcnVnLmZpbmFsLmVmZmVjdC5kYXRhZnJhbWVbLCAtMV0pCiAgcm93bmFtZXMoZGF0YSkgPC0gZHJ1Zy5maW5hbC5lZmZlY3QuZGF0YWZyYW1lJHBvcHVsYXRpb24KICBkYXRhW2lzLm5hKGRhdGEpXSA8LSAwCiAgCiAgIyBSZW9yZ2FuaXppbmcgcm93cyBhbmQgY29sdW1ucwogIHJvd19vcmRlciA8LSBvcmRlci5kZW5kcm9ncmFtKGFzLmRlbmRyb2dyYW0oY2x1c3RlcmluZ19yb3dzKSkKICBjb2xfb3JkZXIgPC0gb3JkZXIuZGVuZHJvZ3JhbShhcy5kZW5kcm9ncmFtKGNsdXN0ZXJpbmdfY29scykpCiAgZGF0YSA8LSBkYXRhW3Jvd19vcmRlciwgY29sX29yZGVyXQogIAogICMgUGxvdAogIHBoZWF0bWFwKGRhdGEsIAogICAgICAgICAgIGNvbG9yID0gbXlfY29sb3JzLAogICAgICAgICAgIG5hX2NvbG9yID0gImdyZXkiLAogICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IGRlbmRybywgCiAgICAgICAgICAgY2x1c3Rlcl9jb2xzID0gZGVuZHJvLAogICAgICAgICAgIG1haW4gPSBkcnVnLm5hbWUsCiAgICAgICAgICAgY2VsbHdpZHRoID0gMTAsIAogICAgICAgICAgIGNlbGxoZWlnaHQgPSAxMCwKICAgICAgICAgICBsZWdlbmRfYnJlYWtzID0gYygtMSwgLTAuNSwgMCwgMC41LCAxKSwKICAgICAgICAgICBsZWdlbmRfbGFiZWxzID0gYygiRHJ1ZyBlZmZlY3QgY29saW5lYXJcbnRvIG5vcm1hbCBwcmVnbmFuY3kiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNaXNzaW5nIFZhbHVlXG5vclxuTm90IHNpZ25pZmljYW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRHJ1ZyBlZmZlY3Qgb3Bwb3NpdGVcbnRvIG5vcm1hbCBwcmVnbmFuY3kiKQogICAgICAgICAgICkKfQpgYGAKClNhdmluZyB0aGUgaGVhdCBtYXBzIGZvciBhIGNob3NlbiBzZXQgb2YgdGhyZXNob2xkcy4KCmBgYHtyfQojIFBhcmFtZXRlcnMgZm9yIHRoZSBoZWF0IG1hcCBzY29yZXMKVFRMIDwtIC04NApub3JtYWwuYmVoYXZpb3IucHZhbC50aHJlc2hvbGQgPC0gMS4Kbm9ybWFsLmJlaGF2aW9yLnNsb3BlLnRocmVzaG9sZCA8LSAwLgpkcnVnLnB2YWwudGhyZXNob2xkIDwtIDEuCmRydWcuc2xvcGUudGhyZXNob2xkIDwtIDAuCmZlYXR1cmVzLm9mLmludGVyZXN0IDwtIG5hbWVzKHRvcC5mZWF0dXJlLmluZGV4KQpyZWYuY2x1c3RlcmluZy5mZWF0dXJlIDwtICJNZXRmb3JtaW4iCgojIEJ1aWxkaW5nIHRoZSBjbHVzdGVyaW5nIG9mIHJlZmVyZW5jZSBmb3IgYWxsIHRoZSBoZWF0bWFwcwpyZWZfZGF0YSA8LSBjcmVhdGUuZHJ1Zy5lZmZlY3QuZGF0YS5mcmFtZSgKICAgICAgICAgICAgICAgICAgcmVmLmNsdXN0ZXJpbmcuZmVhdHVyZSwKICAgICAgICAgICAgICAgICAgbm9ybWFsLmJlaGF2aW9yLnB2YWwudGhyZXNob2xkLCAKICAgICAgICAgICAgICAgICAgbm9ybWFsLmJlaGF2aW9yLnNsb3BlLnRocmVzaG9sZCwgCiAgICAgICAgICAgICAgICAgIGRydWcucHZhbC50aHJlc2hvbGQsIAogICAgICAgICAgICAgICAgICBkcnVnLnNsb3BlLnRocmVzaG9sZCwKICAgICAgICAgICAgICAgICAgVFRMLAogICAgICAgICAgICAgICAgICBmZWF0dXJlcy5vZi5pbnRlcmVzdCkKZGF0YSA8LSBhcy5tYXRyaXgocmVmX2RhdGFbLCAtMV0pCnJvd25hbWVzKGRhdGEpIDwtIHJlZl9kYXRhJHBvcHVsYXRpb24KZGF0YVtpcy5uYShkYXRhKV0gPC0gMApjbHVzdGVyaW5nX3Jvd3MgPC0gaGNsdXN0KGRpc3QoZGF0YSkpCmNsdXN0ZXJpbmdfY29scyA8LSBoY2x1c3QoZGlzdCh0KGRhdGEpKSkKCgojIFBsb3R0aW5nIGFsbCB0aGUgaGVhdCBtYXBzCnBkZihwYXN0ZShkcnVnX2Fzc2F5X3BhdGgsICIvcGxvdHMvRHJ1ZyBlZmZlY3QgaGVhdG1hcHMgVFRMPSIsVFRMLCAiLnBkZiIsIHNlcD0iIiksIHdpZHRoID0gMTIsIGhlaWdodCA9IDYpCmZvciAodGFyZ2V0X2RydWcgaW4gdW5pcXVlKGZpbmFsLm1vZGVsJGRydWcpKXsKICBwbG90X3BoZWF0bWFwKHRhcmdldF9kcnVnLCAKICAgICAgICAgICAgICAgIGNyZWF0ZS5kcnVnLmVmZmVjdC5kYXRhLmZyYW1lKAogICAgICAgICAgICAgICAgICB0YXJnZXRfZHJ1ZywKICAgICAgICAgICAgICAgICAgbm9ybWFsLmJlaGF2aW9yLnB2YWwudGhyZXNob2xkLCAKICAgICAgICAgICAgICAgICAgbm9ybWFsLmJlaGF2aW9yLnNsb3BlLnRocmVzaG9sZCwgCiAgICAgICAgICAgICAgICAgIGRydWcucHZhbC50aHJlc2hvbGQsIAogICAgICAgICAgICAgICAgICBkcnVnLnNsb3BlLnRocmVzaG9sZCwKICAgICAgICAgICAgICAgICAgVFRMLAogICAgICAgICAgICAgICAgICBmZWF0dXJlcy5vZi5pbnRlcmVzdCksCiAgICAgICAgICAgICAgICBjbHVzdGVyaW5nX3Jvd3MsCiAgICAgICAgICAgICAgICBjbHVzdGVyaW5nX2NvbHMsCiAgICAgICAgICAgICAgICBkZW5kcm89RkFMU0UpCn0KZGV2Lm9mZigpCmBgYAoKIyMjIEdsb2JhbCB2aXN1YWxpemF0aW9uIDogZHJ1ZyBlZmZlY3RzCgpIZXJlIHdlIHBsb3QgYSBmaWd1cmUgaGVscGluZyB1cyB2aXN1YWxpemUgdGhlIGVmZmVjdCBvZiBvbmUgZHJ1ZyBiYXNlZCBvbiB0aGUgc2xvcGUgb2YgaXRzIGxpbmVhciBtb2RlbCBhY3Jvc3MgYWxsIGZlYXR1cmVzLgoKYGBge3J9CnNsb3BlX3RocmVzaG9sZCA8LSAyZS0xICMgQWJvdmUgdGhpcyB0aHJlc2hvbGQgdGhlIGNvbG9yIG9mIHRoZSBwbG90IHdpbGwgYmUgdGhlIG1vc3QgaW50ZW5zZSBvbmUKcHZhbF90aHJlc2hvbGQgPC0gNWUtMiAjIFVuZGVyIHRoaXMgdGhyZXNob2xkIHRoZSBzaXplIG9mIHRoZSBub2RlIHdpbGwgYmUgdGhlIGJpZ2dlc3Qgb25lIChpZiB0YWtpbmcgcHZhbHVlIGZvciBzaXplKQpybXNlX3RocmVzaG9sZCA8LSAxZS0yICMgVW5kZXIgdGhpcyB0aHJlc2hvbGQgdGhlIHNpemUgb2YgdGhlIG5vZGUgd2lsbCBiZSB0aGUgYmlnZ2VzdCBvbmUgKGlmIHRha2luZyBybXNlIGZvciBzaXplKQpwcmVjaXNpb24gPC0gMC4gIyBVbmRlciB0aGlzIHRocmVzaG9sZCB0aGUgdmFsdWVzIGluIHRoZSBkYXRhc2V0IG9uIHdoaWNoIHRoZSBtb2RlbCBpcyBidWlsdCBpcyBub3QgcmVsZXZhbnQgKFNsb3BlX2ludGVuc2l0eSA8LSAwKQoKZmluYWwubW9kZWwuaW5mby5kZWNvbXAgPC0gZmluYWwubW9kZWwgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5KGZlYXR1cmUsIGRydWcpICU+JQogICAgICAgICAgICAgICAgICAgICAgICBzbGljZSgxKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKCMgUmV0cmlldmluZyB0aGUgc2VwYXJhdGlvbiA6IHBvcHVsYXRpb24sIHJlYWdlbnQsIHN0aW11bGF0aW9uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uID0gc3ViKCJeKC4qPylfLioiLCAiXFwxIiwgYXMuY2hhcmFjdGVyKGZlYXR1cmUpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWdlbnQgPSBzdWIoIi4qP18oLio/KV8uKiIsICJcXDEiLCBhcy5jaGFyYWN0ZXIoZmVhdHVyZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RpbXVsYXRpb24gPSBzdWIoIi4qXy4qXyguKikiLCAiXFwxIiwgYXMuY2hhcmFjdGVyKGZlYXR1cmUpKSwKCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIElmIHRoZSBhYnNvbHV0ZSB2YWx1ZSBvZiB0aGUgc2xvcGUgaXMgZ3JlYXRlciB0aGFuIHRoZSB0aHJlc2hvbGQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgc2xvcGUgaXMgcmVwbGFjZWQgYnkgdGhlIHRocmVzaG9sZCAod2l0aCB0aGUgYWRlcXVhdGUgc2lnbikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNsb3BlID0gaWZlbHNlKHNsb3BlID4gc2xvcGVfdGhyZXNob2xkLCBzbG9wZV90aHJlc2hvbGQsIHNsb3BlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNsb3BlID0gaWZlbHNlKHNsb3BlIDwgLXNsb3BlX3RocmVzaG9sZCwgLXNsb3BlX3RocmVzaG9sZCwgc2xvcGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBJZiB0aGUgcHZhbHVlIGlzIHNtYWxsZXIgdGhhbiB0aGUgdGhyZXNob2xkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIHB2YWx1ZSBpcyByZXBsYWNlZCBieSB0aGUgdGhyZXNob2xkCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsID0gaWZlbHNlKHB2YWwgPCBwdmFsX3RocmVzaG9sZCwgcHZhbF90aHJlc2hvbGQsIHB2YWwpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBsb2cgdHJhbnNmb3JtIG5vcm1hbGl6ZWQgYnkgdGhlIHRocmVzaG9sZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nX3B2YWwgPSBsb2cxMChwdmFsKS9sb2cxMChwdmFsX3RocmVzaG9sZCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIElmIHRoZSBybXNlIGlzIHNtYWxsZXIgdGhhbiB0aGUgdGhyZXNob2xkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIHJtc2UgaXMgcmVwbGFjZWQgYnkgdGhlIHRocmVzaG9sZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUk1TRSA9IGlmZWxzZShybXNlIDwgcm1zZV90aHJlc2hvbGQsIHJtc2VfdGhyZXNob2xkLCBybXNlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbG9nIHRyYW5zZm9ybSBub3JtYWxpemVkIGJ5IHRoZSB0aHJlc2hvbGQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ19STVNFID0gbG9nMTAoUk1TRSkvbG9nMTAocm1zZV90aHJlc2hvbGQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTbG9wZV9pbnRlbnNpdHkgPSBTbG9wZS8obWF4KHN1YnNldCBkYXRhKSAtIG1pbihzdWJzZXQgZGF0YSkpIG9ubHkgd2hlbiB0aGUgbWVhc3VyZXMgYXJlIHNpZ25pZmljYW50CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhfdmFsID0gbWF4KEFsbFBlbkRvc2VyZXNwb25zZVtBbGxQZW5Eb3NlcmVzcG9uc2UkZHJ1ZyA9PSBkcnVnICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFsbFBlbkRvc2VyZXNwb25zZSRmZWF0dXJlID09IGZlYXR1cmUsXSR2YWx1ZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fdmFsID0gbWluKEFsbFBlbkRvc2VyZXNwb25zZVtBbGxQZW5Eb3NlcmVzcG9uc2UkZHJ1ZyA9PSBkcnVnICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFsbFBlbkRvc2VyZXNwb25zZSRmZWF0dXJlID09IGZlYXR1cmUsXSR2YWx1ZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTbG9wZV9pbnRlbnNpdHkgPSBpZmVsc2UobWF4X3ZhbCAtIG1pbl92YWwgPCBwcmVjaXNpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTbG9wZS8obWF4X3ZhbCAtIG1pbl92YWwpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBDb29yZGluYXRlcyBmb3IgdGhlIHBsb3Qgb2YgdGhlIHJlYWdlbnRzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIyBTVEFUIGZpcnN0IGNvbHVtbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IGlmZWxzZShncmVwbCgiU1RBVCIsIHJlYWdlbnQpLCAxLjUsIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyMgU1RBVDEgKDEsNCkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBpZmVsc2UocmVhZ2VudCA9PSAiU1RBVDEiLCA0LCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMjIFNUQVQzICgxLDMpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gaWZlbHNlKHJlYWdlbnQgPT0gIlNUQVQzIiwgMywgeSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIyBTVEFUNSAoMSwyKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGlmZWxzZShyZWFnZW50ID09ICJTVEFUNSIsIDIsIHkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyMgU1RBVDYgKDEsMSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBpZmVsc2UocmVhZ2VudCA9PSAiU1RBVDYiLCAxLCB5KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMjIE5Ga0IgKDMsNCkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBpZmVsc2UocmVhZ2VudCA9PSAiTkZrQiIsIDMsIHgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGlmZWxzZShyZWFnZW50ID09ICJORmtCIiwgNCwgeSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIyBFUksgKDMsMykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBpZmVsc2UocmVhZ2VudCA9PSAiRVJLIiwgMywgeCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gaWZlbHNlKHJlYWdlbnQgPT0gIkVSSyIsIDMsIHkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyMgUzYgKDMsMikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBpZmVsc2UocmVhZ2VudCA9PSAiUzYiLCAzLCB4KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBpZmVsc2UocmVhZ2VudCA9PSAiUzYiLCAyLCB5KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMjIE1BUEtBUEsyICgzLDEpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gaWZlbHNlKHJlYWdlbnQgPT0gIk1BUEtBUEsyIiwgMywgeCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gaWZlbHNlKHJlYWdlbnQgPT0gIk1BUEtBUEsyIiwgMSwgeSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIyBJa0IgKDQsNCkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBpZmVsc2UocmVhZ2VudCA9PSAiSWtCIiwgNCwgeCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gaWZlbHNlKHJlYWdlbnQgPT0gIklrQiIsIDQsIHkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyMgQ1JFQiAoNCwzKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IGlmZWxzZShyZWFnZW50ID09ICJDUkVCIiwgNCwgeCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gaWZlbHNlKHJlYWdlbnQgPT0gIkNSRUIiLCAzLCB5KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMjIHAzOCAoNCwyKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IGlmZWxzZShyZWFnZW50ID09ICJwMzgiLCA0LCB4KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBpZmVsc2UocmVhZ2VudCA9PSAicDM4IiwgMiwgeSkpCmBgYAoKSGVyZSB3ZSBhcmUgZG9pbmcgb25lIG9mIHRoZSAxMDAgcGxvdHMgdGhhdCBhcmUgZ29pbmcgdG8gYmUgYnVpbHQgZm9yIGVhY2ggZHJ1ZyBzbyB0aGF0IHdlIGNhbiBwbG90IHRoZSBsZWdlbmQgYXMgd2VsbC4gRWFjaCBub2RlIHJlcHJlc2VudCBhIHJlYWdlbnQsIGl0cyBzaXplIGlzIHByb3BvcnRpb25hbCB0byAkLWxvZ197MTB9KHBfe3ZhbH0pJCwgaXRzIGNvbG9yIGlzIAoKYGBge3J9CiMgRXhhbXBsZSBvZiBzZXR0aW5ncyBmb3IgdGhlIHBsb3QKZXhhbXBsZV9kcnVnIDwtICJGb2xpYyBhY2lkIgpleGFtcGxlX3BvcHVsYXRpb24gPC0gIkNENFRlbSIKZXhhbXBsZV9zdGltdWxhdGlvbiA8LSAiSUZOYSIKCiMgU3Vic2V0CmRmIDwtIGZpbmFsLm1vZGVsLmluZm8uZGVjb21wW2ZpbmFsLm1vZGVsLmluZm8uZGVjb21wJGRydWcgPT0gZXhhbXBsZV9kcnVnICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmluYWwubW9kZWwuaW5mby5kZWNvbXAkcG9wdWxhdGlvbiA9PSBleGFtcGxlX3BvcHVsYXRpb24gJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbmFsLm1vZGVsLmluZm8uZGVjb21wJHN0aW11bGF0aW9uID09IGV4YW1wbGVfc3RpbXVsYXRpb24sXQoKIyBQbG90CnAgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IHgsIHkgPSB5KSkgKwogICMgTm9kZXMgc2l6ZSwgYm9yZGVyIGFuZCBjb2xvcgogIGdlb21fcG9pbnQoYWVzKHNpemUgPSBsb2dfcHZhbCwgZmlsbCA9IFNsb3BlX2ludGVuc2l0eSksIHNoYXBlID0gMjEpICsKICAjIE5hbWUgb2YgdGhlIG5vZGVzCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJlYWdlbnQpLCB2anVzdCA9IC0xLjUsIGhqdXN0ID0gMC41LCBzaXplID0gNSwgY29sb3IgPSAiYmxhY2siKSArCiAgIyBTY2FsZSBvZiB0aGUgc2l6ZSBvZiB0aGUgbm9kZXMKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDIsIDEwKSwgbGltaXRzID0gYygwLCAxKSkgKwogICMgU2NhbGUgb2YgdGhlIGNvbG9yIG9mIHRoZSBub2RlcwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJibHVlIiwgbWlkID0gIndoaXRlIiwgaGlnaCA9ICJyZWQiLCBtaWRwb2ludCA9IDAsIGxpbWl0cyA9IGMoLTAuMywgMC4zKSkgKwogICMgTGVnZW5kIHNldHRpbmdzIChwb3NpdGlvbiBhbmQgYmFja2dyb3VuZCkKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgbGVnZW5kLmJveCA9ICJob3Jpem9udGFsIiwgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICMgQXhpcwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAuNSwgNSksIGV4cGFuZCA9IGMoMCwgMCkpICsgICMgU2V0IHgtYXhpcyBsaW1pdHMgYW5kIHJlbW92ZSBleHBhbnNpb24KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLjUsIDQuNSksIGV4cGFuZCA9IGMoMC4sIDAuKSkgKwogICMgTGFibGVzCiAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwsIHNpemUgPSAibG9nX3B2YWwiLCBmaWxsID0gIlNsb3BlX2ludGVuc2l0eSIsIGNvbG9yID0gIkNsYXNzIikKCiMgU2F2aW5nIHRoZSBwbG90Cmdnc2F2ZShwYXN0ZTAoZHJ1Z19hc3NheV9wYXRoLCAiL3Bsb3RzL2xlZ2VuZF9ncmlkX2VmZmVjdF9wbG90LnBuZyIpLCBwbG90ID0gcCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKYGBgCgpQbG90dGluZyBhIGdyaWQgY29tcG9zZWQgb2Ygc3VicGxvdHMgbGlrZSB0aGUgb25lIGFib3ZlIGZvciBldmVyeSBwb3B1bGF0aW9uIGFuZCBhdmVyeSBzdGltdWxhdGlvbiBhbmQgZWFjaCBkcnVnCgpgYGB7cn0KIyBVc2VmdWwgdmFyaWFibGVzICh3ZSByZXdyaXRlIHRoZW0gYmVjYXVzZSB0aGUgb3JkZXIgaXMgaW1wb3J0YW50IGZvciB0aGUgZmlndXJlcykKc3RpbXVsYXRpb25zIDwtIGMoIkdNQ1NGIiwgIklGTmEiLCAiSUwyNDYiLCAiTFBTIiwgInVuc3RpbSIpCnBvcHVsYXRpb25zIDwtIGMoIkJjZWxscyIsICJDRDRUY20iLCAiQ0Q0VGVmZiIsICJDRDRUZW0iLCAiQ0Q0VG5haXZlIiwgIkNENG5lZ0NEOG5lZ1RjZWxscyIsICJDRDU2aGlDRDE2bmVnTksiLCAiQ0Q1NmxvQ0QxNnBvc05LIiwgIkNEOFRjbSIsICJDRDhUZWZmIiwgIkNEOFRlbSIsICJDRDhUbmFpdmUiLCAiR3JhbnVsb2N5dGVzIiwgIk1EU0NzIiwgIk5LVCIsICJUcmVncyIsICJpbnRNQ3MiLCAibURDcyIsICJuY01DcyIsICJwRENzIikKRHJ1Z3MgPC0gYygiQ2Vmb3RheGltZSIsICJMYW5zb3ByYXpvbGUiLCAiSW9wYW1pZG9sIiwgIklvaGV4b2wiLCAiQmVuenlscGVuaWNpbGxpbiIsICJDaGxvcnRoYWxpZG9uZSIsICJSaWZhYnV0aW4iLCAiSW9kaXhhbm9sIiwgIk1ldGZvcm1pbiIsICJGb2xpYyBhY2lkIiwgIkNsb3RyaW1hem9sZSIsICJNYXByb3RpbGluZSIsICJQcm9nZXN0ZXJvbmUiLCAiUHJhdmFzdGF0aW4iLCAiTWV0aHlscHJlZG9uaXNvbG9uZSIpCmZlYXR1cmVzLm9mLmludGVyZXN0IDwtIG5hbWVzKHRvcC5mZWF0dXJlLmluZGV4KQogIApwbG90X2RydWdfZ3JpZF9lZmZlY3QgPC0gZnVuY3Rpb24odGFyZ2V0X2RydWcsIGZlYXR1cmVzLm9mLmludGVyZXN0KXsKICBwbG90X2xpc3QgPC0gbGlzdCgpCiAgIyBJdGVyYXRlIG92ZXIgdGhlIHJvd3MgYW5kIGNvbHVtbnMgdG8gY3JlYXRlIHRoZSBwbG90cwogIHBvcF90aXRsZSA8LSBUUlVFCiAgZm9yIChzdGltIGluIHN0aW11bGF0aW9ucykgewogICAgc3RpbV90aXRsZSA8LSBUUlVFCiAgICBmb3IgKHBvcHVsYXRpb24gaW4gcG9wdWxhdGlvbnMpIHsKICAgICAgIyBGaWx0ZXIgdGhlIGRhdGEgZm9yIHRoZSBjdXJyZW50IHBvcHVsYXRpb24gYW5kIHN0aW11bGF0aW9uCiAgICAgIHN1YnNldF9kZiA8LSBmaW5hbC5tb2RlbC5pbmZvLmRlY29tcFtmaW5hbC5tb2RlbC5pbmZvLmRlY29tcCRkcnVnID09IHRhcmdldF9kcnVnICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmluYWwubW9kZWwuaW5mby5kZWNvbXAkcG9wdWxhdGlvbiA9PSBwb3B1bGF0aW9uICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmluYWwubW9kZWwuaW5mby5kZWNvbXAkc3RpbXVsYXRpb249PXN0aW0sXSAlPiUKICAgICAgICAgICAgICAgICAgICBtdXRhdGUob2YuaW50ZXJlc3QgPSBpZmVsc2UoZmVhdHVyZSAlaW4lIGZlYXR1cmVzLm9mLmludGVyZXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiWWVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5vIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlci50aGlja25lc3MgPSBpZmVsc2UoZmVhdHVyZSAlaW4lIGZlYXR1cmVzLm9mLmludGVyZXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDIuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEuKSkKICAgICAgCiAgICAgICMgU3VicGxvdCBmb3IgdGhlIGN1cnJlbnQgcG9wdWxhdGlvbiBhbmQgc3RpbXVsYXRpb24KICAgICAgcCA8LSBnZ3Bsb3Qoc3Vic2V0X2RmLCBhZXMoeCA9IHgvMjUsIHkgPSB5LzI1LCBjb2xvcj1vZi5pbnRlcmVzdCwgc3Ryb2tlPWJvcmRlci50aGlja25lc3MpKSArCiAgICAgICAgIyBOb2RlcyBzaXplLCBib3JkZXIgYW5kIGNvbG9yCiAgICAgICAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IGxvZ19wdmFsLCBmaWxsID0gU2xvcGVfaW50ZW5zaXR5LCBjb2xvcj1vZi5pbnRlcmVzdCwgc3Ryb2tlPWJvcmRlci50aGlja25lc3MpLCBzaGFwZSA9IDIxKSArCiAgICAgICAgIyBTY2FsZSBvZiB0aGUgc2l6ZSBvZiB0aGUgbm9kZXMKICAgICAgICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDIsIDEwKSwgbGltaXRzID0gYygwLCAxKSkgKwogICAgICAgICMgU2NhbGUgb2YgdGhlIGNvbG9yIG9mIHRoZSBub2RlcwogICAgICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJibHVlIiwgbWlkID0gIndoaXRlIiwgaGlnaCA9ICJyZWQiLCBtaWRwb2ludCA9IDAsIGxpbWl0cyA9IGMoLTAuMywgMC4zKSkgKwogICAgICAgICMgQm9yZGVyIG9mIHRoZSBub2RlcwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJZZXMiID0gInJlZCIpKSArCiAgICAgICAgIyBBZGRpbmcgYSBib3JkZXIgdG8gdGhlIGZpZ3VyZSBpZiB0aGVyZSBhcmUgZmVhdHVyZXMgb2YgaW50ZXJlc3QgaW5zaWRlCiAgICAgICAgZ2VvbV9yZWN0KGFlcyh4bWluID0gMC4wMiwgeG1heCA9IDAuMiwgeW1pbiA9IDAuLCB5bWF4ID0gMC4yKSwKICAgICAgICAgICAgICAgICAgZmlsbCA9IE5BLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLAogICAgICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJzb2xpZCIsCiAgICAgICAgICAgICAgICAgIHNpemUgPSBpZmVsc2UoYW55KHN1YnNldF9kZiRvZi5pbnRlcmVzdCA9PSAiWWVzIiksIDEuNSwgTkEpKSArCiAgICAgICAgIyBSZW1vdmluZyBsZWdlbmRzIGFuZCBhZGp1c3Rpbmcgc2l6ZSBhbmQgbWFyZ2lucwogICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYsIG1hcmdpbiA9IG1hcmdpbihyID0gMCkpLAogICAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMC41LCAwLiwgMC4sIDAuNSksCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2LCBtYXJnaW4gPSBtYXJnaW4ociA9IC0yKSksCiAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpKSArICAKICAgICAgICAjIExhYmVscyAoeSBsYWJlbCBvbmx5IGFwcGVhciBmb3IgdGhlIGZpcnN0IGNvbHVtbiBvZiBzdWJwbG90IHdpdGggdGhlIG5hbWUgb2YgdGhlIHN0aW0pCiAgICAgICAgbGFicyh4ID0gTlVMTCwgeSA9IGlmZWxzZShzdGltX3RpdGxlLCBzdGltLCAiIiksIHNpemUgPSAibG9nX3B2YWwiLCBmaWxsID0gIlNsb3BlX2ludGVuc2l0eSIsIGNvbG9yID0gIkNsYXNzIikgKwogICAgICAgICMgQXhpcwogICAgICAgIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAuMDIsIDAuMiksIGV4cGFuZCA9IGMoMCwgMCkpICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLiwgMC4yKSwgZXhwYW5kID0gYygwLiwgMC4pKSArCiAgICAgICAgIyBUaXRsZSAob25seSBhcHBlYXIgZm9yIHRoZSBmaXJzdCByb3cgb2Ygc3VicGxvdHMgd2l0aCB0aGUgbmFtZSBvZiB0aGUgcG9wdWxhdGlvbikKICAgICAgICBnZ3RpdGxlKGlmZWxzZShwb3BfdGl0bGUsIHBvcHVsYXRpb24sICIiKSkgKwogICAgICAgICMgUmVtb3ZpbmcKICAgICAgICBndWlkZXMoZmlsbCA9ICJub25lIiwgY29sb3IgPSAibm9uZSIsIHNpemUgPSAibm9uZSIpCiAgICAgIAogICAgICAjIFN0b3JlIHRoZSBwbG90IGluIHRoZSBwbG90IGxpc3QKICAgICAgcGxvdF9saXN0W1tsZW5ndGgocGxvdF9saXN0KSArIDFdXSA8LSBwCiAgICAgIHN0aW1fdGl0bGUgPC0gRkFMU0UKICAgIH0KICAgIHBvcF90aXRsZSA8LSBGQUxTRQogIH0KICAKICAjIENyZWF0aW5nIHRoZSBncmlkIHBsb3QKICBncmlkX2FycmFuZ2UgPC0gZ3JpZC5hcnJhbmdlKGdyb2JzID0gcGxvdF9saXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IGxlbmd0aChwb3B1bGF0aW9ucyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gbGVuZ3RoKHN0aW11bGF0aW9ucykpCiAgIyBBZGRpbmcgYSB0aXRsZSB0byB0aGUgZ3JpZCBwbG90CiAgdGl0bGUgPC0gdGV4dEdyb2IodGFyZ2V0X2RydWcsIGdwID0gZ3Bhcihmb250c2l6ZSA9IDE2LCBmb250ZmFjZSA9ICJib2xkIikpCiAgbGF5b3V0IDwtIHJiaW5kKGMoMSksIGMoMikpCiAgYXJyYW5nZWRfcGxvdHMgPC0gYXJyYW5nZUdyb2IodGl0bGUsIGdyaWRfYXJyYW5nZSwgbGF5b3V0X21hdHJpeCA9IGxheW91dCwgaGVpZ2h0cyA9IGMoMSwgOSkpCiAgCiAgcmV0dXJuKGFycmFuZ2VkX3Bsb3RzKQp9CgojIENyZWF0aW5nIGFuZCBzYXZpbmcgdGhlIGZpZ3VyZXMgaW4gcGRmIGZpbGVzCmZvciAodGFyZ2V0X2RydWcgaW4gRHJ1Z3MpIHsKICBncmlkX3Bsb3QgPC0gcGxvdF9kcnVnX2dyaWRfZWZmZWN0KHRhcmdldF9kcnVnLCBmZWF0dXJlcy5vZi5pbnRlcmVzdCkKICBwZGYocGFzdGUoZHJ1Z19hc3NheV9wYXRoLCAiL3Bsb3RzLyIsIHRhcmdldF9kcnVnLCAiX2dyaWRfZWZmZWN0X3Bsb3RzLnBkZiIsIHNlcD0iIiksIHdpZHRoPTIwLCBoZWlnaHQ9OCkKICBncmlkLmRyYXcoZ3JpZF9wbG90KQogIGRldi5vZmYoKQp9CmBgYAoKCgoK